summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--compiler/ast.nim5
-rw-r--r--compiler/ccgexprs.nim2
-rw-r--r--compiler/ccgmerge.nim7
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtypes.nim2
-rw-r--r--compiler/cgen.nim24
-rw-r--r--compiler/cgendata.nim17
-rw-r--r--compiler/commands.nim12
-rw-r--r--compiler/evaltempl.nim8
-rw-r--r--compiler/hlo.nim2
-rw-r--r--compiler/importer.nim6
-rw-r--r--compiler/jsgen.nim8
-rw-r--r--compiler/lexer.nim23
-rw-r--r--compiler/main.nim3
-rw-r--r--compiler/modules.nim6
-rw-r--r--compiler/msgs.nim4
-rw-r--r--compiler/nim.cfg2
-rw-r--r--compiler/nim.nim4
-rw-r--r--compiler/options.nim15
-rw-r--r--compiler/parser.nim5
-rw-r--r--compiler/pragmas.nim23
-rw-r--r--compiler/renderer.nim6
-rw-r--r--compiler/scriptconfig.nim4
-rw-r--r--compiler/sem.nim7
-rw-r--r--compiler/semcall.nim6
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim97
-rw-r--r--compiler/semfold.nim2
-rw-r--r--compiler/seminst.nim6
-rw-r--r--compiler/semmacrosanity.nim16
-rw-r--r--compiler/semstmts.nim35
-rw-r--r--compiler/semtypes.nim19
-rw-r--r--compiler/sigmatch.nim8
-rw-r--r--compiler/vm.nim20
-rw-r--r--compiler/vmdeps.nim169
-rw-r--r--compiler/vmgen.nim19
-rw-r--r--compiler/vmops.nim68
-rw-r--r--doc/filters.txt7
-rw-r--r--doc/lib.txt3
-rw-r--r--doc/manual/exceptions.txt2
-rw-r--r--doc/manual/lexing.txt2
-rw-r--r--doc/manual/syntax.txt2
-rw-r--r--doc/manual/types.txt2
-rw-r--r--doc/niminst.txt4
-rw-r--r--examples/unix_socket/client.nim6
-rw-r--r--examples/unix_socket/server.nim14
-rw-r--r--koch.nim5
-rw-r--r--lib/core/macros.nim14
-rw-r--r--lib/impure/db_odbc.nim45
-rw-r--r--lib/impure/nre.nim12
-rw-r--r--lib/impure/re.nim2
-rw-r--r--lib/packages/docutils/rst.nim13
-rw-r--r--lib/posix/posix.nim51
-rw-r--r--lib/pure/asyncdispatch.nim49
-rw-r--r--lib/pure/asynchttpserver.nim32
-rw-r--r--lib/pure/collections/LockFreeHash.nim6
-rw-r--r--lib/pure/collections/sequtils.nim2
-rw-r--r--lib/pure/collections/sharedtables.nim53
-rw-r--r--lib/pure/collections/tables.nim45
-rw-r--r--lib/pure/concurrency/cpuload.nim2
-rw-r--r--lib/pure/future.nim8
-rw-r--r--lib/pure/httpclient.nim5
-rw-r--r--lib/pure/json.nim57
-rw-r--r--lib/pure/math.nim356
-rw-r--r--lib/pure/nativesockets.nim4
-rw-r--r--lib/pure/net.nim36
-rw-r--r--lib/pure/osproc.nim177
-rw-r--r--lib/pure/parsecfg.nim204
-rw-r--r--lib/pure/parsecsv.nim11
-rw-r--r--lib/pure/parsexml.nim4
-rw-r--r--lib/pure/pegs.nim2
-rw-r--r--lib/pure/random.nim129
-rw-r--r--lib/pure/rationals.nim22
-rw-r--r--lib/pure/strutils.nim315
-rw-r--r--lib/pure/subexes.nim2
-rw-r--r--lib/pure/times.nim110
-rw-r--r--lib/pure/unicode.nim4
-rw-r--r--lib/pure/unittest.nim2
-rw-r--r--lib/system.nim106
-rw-r--r--lib/system/alloc.nim61
-rw-r--r--lib/system/gc.nim198
-rw-r--r--lib/system/gc2.nim8
-rw-r--r--lib/system/gc_stack.nim40
-rw-r--r--lib/system/jssys.nim6
-rw-r--r--lib/system/osalloc.nim4
-rw-r--r--lib/system/sysio.nim3
-rw-r--r--lib/system/sysstr.nim143
-rw-r--r--lib/system/threads.nim4
-rw-r--r--lib/system/timers.nim2
-rw-r--r--lib/windows/winlean.nim4
-rw-r--r--tests/async/tasyncdiscard.nim2
-rw-r--r--tests/async/tasynctry.nim10
-rw-r--r--tests/ccgbugs/twrong_string_asgn.nim2
-rw-r--r--tests/enum/tenum.nim6
-rw-r--r--tests/float/tfloat4.nim32
-rw-r--r--tests/float/tfloat5.nim15
-rw-r--r--tests/float/tfloat6.nim21
-rw-r--r--tests/float/tfloat7.nim26
-rw-r--r--tests/gc/thavlak.nim457
-rw-r--r--tests/gc/tlists.nim37
-rw-r--r--tests/generics/tgenerictmpl2.nim31
-rw-r--r--tests/js/tclosures.nim2
-rw-r--r--tests/js/test2.nim13
-rw-r--r--tests/js/testtojsstr.nim8
-rw-r--r--tests/lexer/tstrlits.nim6
-rw-r--r--tests/macros/tgettypeinst.nim122
-rw-r--r--tests/manyloc/keineschweine/lib/vehicles.nim2
-rw-r--r--tests/manyloc/nake/nakefile.nim4
-rw-r--r--tests/manyloc/named_argument_bug/main.nim.cfg1
-rw-r--r--tests/manyloc/named_argument_bug/tri_engine/math/circle.nim4
-rw-r--r--tests/manyloc/named_argument_bug/tri_engine/math/vec.nim2
-rw-r--r--tests/overload/tstmtoverload.nim2
-rw-r--r--tests/parallel/twrong_refcounts.nim2
-rw-r--r--tests/parser/tdo.nim79
-rw-r--r--tests/stdlib/tmath.nim2
-rw-r--r--tests/stdlib/tparscfg.nim58
-rw-r--r--tests/stdlib/tunittest.nim2
-rw-r--r--tests/template/t2do.nim7
-rw-r--r--tests/testament/categories.nim5
-rw-r--r--tests/threads/ttryrecv.nim2
-rw-r--r--tests/trmacros/tstatic_t_bug.nim24
-rw-r--r--tests/usingstmt/tthis.nim15
-rw-r--r--tests/vm/meta.nim240
-rw-r--r--tests/vm/tcomponent.nim132
-rw-r--r--todo.txt4
-rw-r--r--web/news.txt34
-rw-r--r--web/website.ini2
129 files changed, 3599 insertions, 887 deletions
diff --git a/.gitignore b/.gitignore
index 1971a23cb..e4cec0ef6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ nimcache/
 *.dylib
 *.zip
 *.iss
+*.log
 
 mapping.txt
 tags
@@ -42,3 +43,7 @@ xcuserdata/
 /testresults.json
 testament.db
 /csources
+
+# Private directories and files (IDEs)
+.*/
+~*
diff --git a/compiler/ast.nim b/compiler/ast.nim
index fecbefd7e..7275aceb1 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -938,7 +938,7 @@ const
   genericParamsPos* = 2
   paramsPos* = 3
   pragmasPos* = 4
-  optimizedCodePos* = 5  # will be used for exception tracking
+  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!
@@ -961,6 +961,9 @@ const
                   skMethod, skConverter}
 
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
+var
+  gMainPackageId*: int
+  gMainPackageNotes*: TNoteKinds
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index f892a3128..9f4beda9e 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1235,7 +1235,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope =
   # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
   # have to call it here first:
   let ti = genTypeInfo(p.module, dest)
-  if tfFinal in dest.flags or (p.module.objHasKidsValid and
+  if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
                                tfObjHasKids notin dest.flags):
     result = "$1.m_type == $2" % [a, ti]
   else:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 2a37257b6..2e77cd2a6 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -107,8 +107,8 @@ proc genMergeInfo*(m: BModule): Rope =
   writeIntSet(m.typeInfoMarker, s)
   s.add("labels:")
   encodeVInt(m.labels, s)
-  s.add(" hasframe:")
-  encodeVInt(ord(m.frameDeclared), s)
+  s.add(" flags:")
+  encodeVInt(cast[int](m.flags), s)
   s.add(tnl)
   s.add("*/")
   result = s.rope
@@ -222,7 +222,8 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
     of "declared":  readIntSet(L, m.declaredThings)
     of "typeInfo":  readIntSet(L, m.typeInfoMarker)
     of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
-    of "hasframe":  m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
+    of "flags":
+      m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
     else: internalError("ccgmerge: unknown key: " & k)
 
 when not defined(nimhygiene):
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 988c923c8..61412ad67 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -16,7 +16,7 @@ const
     # above X strings a hash-switch for strings is generated
 
 proc registerGcRoot(p: BProc, v: PSym) =
-  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2} and
+  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} 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 :-)
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index ab771d240..81af89249 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -18,7 +18,7 @@ proc emulatedThreadVars(): bool =
 proc accessThreadLocalVar(p: BProc, s: PSym) =
   if emulatedThreadVars() and not p.threadVarAccessed:
     p.threadVarAccessed = true
-    p.module.usesThreadVars = true
+    incl p.module.flags, usesThreadVars
     addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
     add(p.procSec(cpsInit),
       ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
@@ -51,7 +51,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     addf(m.s[cfsVars], " $1;$n", [s.loc.r])
 
 proc generateThreadLocalStorage(m: BModule) =
-  if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
+  if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
     for t in items(nimtvDeps): discard getTypeDesc(m, t)
     addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 700e6d148..6553deb66 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -752,7 +752,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
   genCLineDir(result, prc.info)
   # using static is needed for inline procs
   if lfExportLib in prc.loc.flags:
-    if m.isHeaderFile:
+    if isHeaderFile in m.flags:
       result.add "N_LIB_IMPORT "
     else:
       result.add "N_LIB_EXPORT "
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 0a8f89f2e..77be125b6 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -64,8 +64,8 @@ proc isSimpleConst(typ: PType): bool =
       (t.kind == tyProc and t.callConv == ccClosure)
 
 proc useStringh(m: BModule) =
-  if not m.includesStringh:
-    m.includesStringh = true
+  if includesStringh notin m.flags:
+    incl m.flags, includesStringh
     discard lists.includeStr(m.headerFiles, "<string.h>")
 
 proc useHeader(m: BModule, sym: PSym) =
@@ -1011,11 +1011,11 @@ proc genInitCode(m: BModule) =
   add(prc, m.postInitProc.s(cpsLocals))
   add(prc, genSectionEnd(cpsLocals))
 
-  if optStackTrace in m.initProc.options and not m.frameDeclared:
+  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:
-    m.frameDeclared = true
-    if not m.preventStackTrace:
+    incl m.flags, frameDeclared
+    if preventStackTrace notin m.flags:
       var procname = makeCString(m.module.name.s)
       add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
@@ -1032,7 +1032,7 @@ proc genInitCode(m: BModule) =
   add(prc, m.initProc.s(cpsStmts))
   add(prc, m.postInitProc.s(cpsStmts))
   add(prc, genSectionEnd(cpsStmts))
-  if optStackTrace in m.initProc.options and not m.preventStackTrace:
+  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", [])
@@ -1105,7 +1105,7 @@ proc rawNewModule(module: PSym, filename: string): BModule =
   # 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:
   if sfSystemModule in module.flags:
-    result.preventStackTrace = true
+    incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
     excl(result.postInitProc.options, optStackTrace)
 
@@ -1128,9 +1128,11 @@ proc resetModule*(m: BModule) =
   m.forwardedProcs = @[]
   m.typeNodesName = getTempName()
   m.nimTypesName = getTempName()
-  m.preventStackTrace = sfSystemModule in m.module.flags
+  if sfSystemModule in m.module.flags:
+    incl m.flags, preventStackTrace
+  else:
+    excl m.flags, preventStackTrace
   nullify m.s
-  m.usesThreadVars = false
   m.typeNodes = 0
   m.nimTypes = 0
   nullify m.extensionLoaders
@@ -1175,7 +1177,7 @@ proc myOpen(module: PSym): PPassContext =
     let f = if headerFile.len > 0: headerFile else: gProjectFull
     generatedHeader = rawNewModule(module,
       changeFileExt(completeCFilePath(f), hExt))
-    generatedHeader.isHeaderFile = true
+    incl generatedHeader.flags, isHeaderFile
 
 proc writeHeader(m: BModule) =
   var result = getCopyright(m.filename)
@@ -1307,7 +1309,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   registerModuleToMain(m.module)
 
   if sfMainModule in m.module.flags:
-    m.objHasKidsValid = true
+    incl m.flags, objHasKidsValid
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 187186373..c098902a6 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -92,17 +92,20 @@ type
     gcFrameType*: Rope       # the struct {} we put the GC markers into
 
   TTypeSeq* = seq[PType]
+
+  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
+                        # a frame var twice in an init proc
+    isHeaderFile,       # C source file is the header file
+    includesStringh,    # C source file already includes ``<string.h>``
+    objHasKidsValid     # whether we can rely on tfObjHasKids
   TCGen = object of TPassContext # represents a C source file
     module*: PSym
     filename*: string
     s*: TCFileSections        # sections of the C file
-    preventStackTrace*: bool  # true if stack traces need to be prevented
-    usesThreadVars*: bool     # true if the module uses a thread var
-    frameDeclared*: bool      # hack for ROD support so that we don't declare
-                              # a frame var twice in an init proc
-    isHeaderFile*: bool       # C source file is the header file
-    includesStringh*: bool    # C source file already includes ``<string.h>``
-    objHasKidsValid*: bool    # whether we can rely on tfObjHasKids
+    flags*: set[Codegenflag]
     cfilename*: string        # filename of the module (including path,
                               # without extension)
     typeCache*: TIdTable      # cache the generated types
diff --git a/compiler/commands.nim b/compiler/commands.nim
index aa155ea88..3bc0b604a 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -213,6 +213,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
     of "size": result = contains(gOptions, optOptimizeSize)
     of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {}
     else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
+  of "verbosity": result = $gVerbosity == arg
   else: invalidCmdLineOption(passCmd1, switch, info)
 
 proc testCompileOption*(switch: string, info: TLineInfo): bool =
@@ -261,13 +262,7 @@ proc processPath(path: string, info: TLineInfo,
           else:
             options.gProjectPath / path
   try:
-    result = unixToNativePath(p % ["nimrod", getPrefixDir(),
-      "nim", getPrefixDir(),
-      "lib", libpath,
-      "home", removeTrailingDirSep(os.getHomeDir()),
-      "config", info.toFullPath().splitFile().dir,
-      "projectname", options.gProjectName,
-      "projectpath", options.gProjectPath])
+    result = pathSubs(p, info.toFullPath().splitFile().dir)
   except ValueError:
     localError(info, "invalid path: " & p)
     result = p
@@ -321,6 +316,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "nonimblepath", "nobabelpath":
     expectNoArg(switch, arg, pass, info)
     options.gNoNimblePath = true
+    options.lazyPaths.head = nil
+    options.lazyPaths.tail = nil
+    options.lazyPaths.counter = 0
   of "excludepath":
     expectArg(switch, arg, pass, info)
     let path = processPath(arg, info)
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index a5a132005..e136265da 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -59,7 +59,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
       evalTemplateAux(templ.sons[i], actual, c, res)
     result.add res
 
-proc evalTemplateArgs(n: PNode, s: PSym): PNode =
+proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
   # if the template has zero arguments, it can be called without ``()``
   # `n` is then a nkSym or something similar
   var totalParams = case n.kind
@@ -75,7 +75,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): 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: 0
+    genericParams = if sfImmediate in s.flags or fromHlo: 0
                     else: s.ast[genericParamsPos].len
     expectedRegularParams = <s.typ.len
     givenRegularParams = totalParams - genericParams
@@ -104,14 +104,14 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
 var evalTemplateCounter* = 0
   # to prevent endless recursion in templates instantiation
 
-proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
+proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
   inc(evalTemplateCounter)
   if evalTemplateCounter > 100:
     globalError(n.info, errTemplateInstantiationTooNested)
     result = n
 
   # replace each param by the corresponding node:
-  var args = evalTemplateArgs(n, tmpl)
+  var args = evalTemplateArgs(n, tmpl, fromHlo)
   var ctx: TemplCtx
   ctx.owner = tmpl
   ctx.genSymOwner = genSymOwner
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 6cc9567af..de0fa6216 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -24,7 +24,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
   of skMacro:
     result = semMacroExpr(c, n, orig, s)
   of skTemplate:
-    result = semTemplateExpr(c, n, s)
+    result = semTemplateExpr(c, n, s, {efFromHlo})
   else:
     result = semDirectOp(c, n, {})
   if optHints in gOptions and hintPattern in gNotes:
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 86993358b..5ffe12728 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -22,7 +22,11 @@ proc getModuleName*(n: PNode): string =
   # The proc won't perform any checks that the path is actually valid
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
-    result = unixToNativePath(n.strVal)
+    try:
+      result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
+    except ValueError:
+      localError(n.info, "invalid path: " & n.strVal)
+      result = n.strVal
   of nkIdent:
     result = n.ident.s
   of nkSym:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 964752c16..124459306 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -71,7 +71,7 @@ type
     isLoop: bool             # whether it's a 'block' or 'while'
 
   TGlobals = object
-    typeInfo, code: Rope
+    typeInfo, constants, code: Rope
     forwarded: seq[PSym]
     generatedSyms: IntSet
     typeInfoGenerated: IntSet
@@ -237,7 +237,7 @@ proc useMagic(p: PProc, name: string) =
     internalAssert s.kind in {skProc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
       let code = genProc(p, s)
-      add(p.g.code, code)
+      add(p.g.constants, code)
   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
@@ -1461,7 +1461,7 @@ proc genConstant(p: PProc, c: PSym) =
     p.body = nil
     #genLineDir(p, c.ast)
     genVarInit(p, c, c.ast)
-    add(p.g.code, p.body)
+    add(p.g.constants, p.body)
     p.body = oldBody
 
 proc genNew(p: PProc, n: PNode) =
@@ -2137,7 +2137,7 @@ proc wholeCode*(m: BModule): Rope =
       var p = newProc(globals, m, nil, m.module.options)
       attachProc(p, prc)
 
-  result = globals.typeInfo & globals.code
+  result = globals.typeInfo & globals.constants & globals.code
 
 proc getClassName(t: PType): Rope =
   var s = t.sym
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 0a4c01ba8..8b201431e 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -138,6 +138,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
 proc isKeyword*(kind: TTokType): bool =
   result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
 
+template ones(n: expr): expr = ((1 shl n)-1) # for utf-8 conversion
+
 proc isNimIdentifier*(s: string): bool =
   if s[0] in SymStartChars:
     var i = 1
@@ -589,12 +591,29 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
   of '\\':
     add(tok.literal, '\\')
     inc(L.bufpos)
-  of 'x', 'X':
+  of 'x', 'X', 'u', 'U':
+    var tp = L.buf[L.bufpos]
     inc(L.bufpos)
     var xi = 0
     handleHexChar(L, xi)
     handleHexChar(L, xi)
-    add(tok.literal, chr(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" )
+    else:
+      add(tok.literal, chr(xi))
   of '0'..'9':
     if matchTwoChars(L, '0', {'0'..'9'}):
       lexMessage(L, warnOctalEscape)
diff --git a/compiler/main.nim b/compiler/main.nim
index ece21a817..b1b9006bd 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -41,6 +41,7 @@ proc commandGenDepend =
 
 proc commandCheck =
   msgs.gErrorMax = high(int)  # do not stop after first error
+  defineSymbol("nimcheck")
   semanticPasses()            # use an empty backend for semantic checking only
   rodPass()
   compileProject()
@@ -241,7 +242,7 @@ proc mainCommand* =
   clearPasses()
   gLastCmdTime = epochTime()
   appendStr(searchPaths, options.libpath)
-  if gProjectFull.len != 0:
+  when false: # gProjectFull.len != 0:
     # current path is always looked first for modules
     prependStr(searchPaths, gProjectPath)
   setId(100)
diff --git a/compiler/modules.nim b/compiler/modules.nim
index ef727e200..8ac964321 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -156,6 +156,9 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
     #var rd = handleSymbolFile(result)
     var rd: PRodReader
     result.flags = result.flags + flags
+    if sfMainModule in result.flags:
+      gMainPackageId = result.owner.id
+
     if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
       rd = handleSymbolFile(result)
       if result.id < 0:
@@ -183,6 +186,9 @@ proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} =
   if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
   if sfSystemModule in result.flags:
     localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
+           else: ForeignPackageNotes
 
 proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
   result = syntaxes.parseFile(fileIdx)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a556ad0c5..668d43bb3 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -500,12 +500,14 @@ type
   ESuggestDone* = object of Exception
 
 const
+  ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
+    hintQuitCalled}
   NotesVerbosity*: array[0..3, TNoteKinds] = [
     {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
                                          warnProveField, warnProveIndex,
                                          warnGcUnsafe,
                                          hintSuccessX, hintPath, hintConf,
-                                         hintProcessing,
+                                         hintProcessing, hintPattern,
                                          hintDependency,
                                          hintExecuting, hintLinking,
                                          hintCodeBegin, hintCodeEnd,
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 4f9962ea8..0ff128ba3 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -7,7 +7,7 @@ path:"$projectPath/.."
 path:"$lib/packages/docutils"
 
 define:booting
-import:testability
+#import:"$projectpath/testability"
 
 @if windows:
   cincludes: "$lib/wrappers/libffi/common"
diff --git a/compiler/nim.nim b/compiler/nim.nim
index b242052ec..a58afd593 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -56,12 +56,12 @@ proc handleCmdLine() =
     loadConfigs(DefaultConfig) # load all config files
     let scriptFile = gProjectFull.changeFileExt("nims")
     if fileExists(scriptFile):
-      runNimScript(scriptFile)
+      runNimScript(scriptFile, freshDefines=false)
       # 'nim foo.nims' means to just run the NimScript file and do nothing more:
       if scriptFile == gProjectFull: return
     elif fileExists(gProjectPath / "config.nims"):
       # directory wide NimScript file
-      runNimScript(gProjectPath / "config.nims")
+      runNimScript(gProjectPath / "config.nims", freshDefines=false)
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
diff --git a/compiler/options.nim b/compiler/options.nim
index 2716a98d3..3ef6c6c46 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -242,6 +242,21 @@ proc getNimcacheDir*: string =
   result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
                                                          genSubDir
 
+
+proc pathSubs*(p, config: string): string =
+  let home = removeTrailingDirSep(os.getHomeDir())
+  result = unixToNativePath(p % [
+    "nim", getPrefixDir(),
+    "lib", libpath,
+    "home", home,
+    "config", config,
+    "projectname", options.gProjectName,
+    "projectpath", options.gProjectPath,
+    "projectdir", options.gProjectPath,
+    "nimcache", getNimcacheDir()])
+  if '~' in result:
+    result = result.replace("~", home)
+
 template newPackageCache(): expr =
   newStringTable(when FileSystemCaseSensitive:
                    modeCaseInsensitive
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 1ba59b938..6132216e1 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -964,8 +964,9 @@ proc parseDoBlock(p: var TParser): PNode =
 proc parseDoBlocks(p: var TParser, call: PNode) =
   #| doBlocks = doBlock ^* IND{=}
   if p.tok.tokType == tkDo:
-    addSon(call, parseDoBlock(p))
-    while sameInd(p) and p.tok.tokType == tkDo:
+    #withInd(p):
+    #  addSon(call, parseDoBlock(p))
+    while sameOrNoInd(p) and p.tok.tokType == tkDo:
       addSon(call, parseDoBlock(p))
 
 proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 38d17eb62..dc618d9aa 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -277,6 +277,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
 proc processNote(c: PContext, n: PNode) =
   if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
       (n.sons[0].kind == nkBracketExpr) and
+      (n.sons[0].sons.len == 2) and
       (n.sons[0].sons[1].kind == nkIdent) and
       (n.sons[0].sons[0].kind == nkIdent):
       #and (n.sons[1].kind == nkIdent):
@@ -392,21 +393,23 @@ type
   TLinkFeature = enum
     linkNormal, linkSys
 
-proc processCompile(c: PContext, n: PNode) =
+proc relativeFile(c: PContext; n: PNode; ext=""): string =
   var s = expectStrLit(c, n)
-  var found = findFile(s)
-  if found == "": found = s
-  var trunc = changeFileExt(found, "")
-  if not isAbsolute(found):
-    found = parentDir(n.info.toFullPath) / found
+  if ext.len > 0 and splitFile(s).ext == "":
+    s = addFileExt(s, ext)
+  result = parentDir(n.info.toFullPath) / s
+  if not fileExists(result):
+    if isAbsolute(s): result = s
+    else: result = findFile(s)
+
+proc processCompile(c: PContext, n: PNode) =
+  let found = relativeFile(c, n)
+  let trunc = found.changeFileExt("")
   extccomp.addExternalFileToCompile(found)
   extccomp.addFileToLink(completeCFilePath(trunc, false))
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
-  var f = expectStrLit(c, n)
-  if splitFile(f).ext == "": f = addFileExt(f, CC[cCompiler].objExt)
-  var found = findFile(f)
-  if found == "": found = f # use the default
+  let found = relativeFile(c, n, CC[cCompiler].objExt)
   case feature
   of linkNormal: extccomp.addFileToLink(found)
   of linkSys:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index f0ee137e9..e1db327f4 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -712,7 +712,11 @@ proc gproc(g: var TSrcGen, n: PNode) =
     gpattern(g, n.sons[patternPos])
   let oldCheckAnon = g.checkAnon
   g.checkAnon = true
-  gsub(g, n.sons[genericParamsPos])
+  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.sons[genericParamsPos])
   g.checkAnon = oldCheckAnon
   gsub(g, n.sons[paramsPos])
   gsub(g, n.sons[pragmasPos])
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 22cd282fd..d04fd5231 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -118,10 +118,10 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext =
     processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
 
 
-proc runNimScript*(scriptName: string) =
+proc runNimScript*(scriptName: string; freshDefines=true) =
   passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
-  initDefines()
+  if freshDefines: initDefines()
 
   defineSymbol("nimscript")
   defineSymbol("nimconfig")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index c29cbe384..a8ec2229f 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -354,7 +354,6 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-
   result = evalMacroCall(c.module, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, result, sym, flags)
@@ -414,6 +413,12 @@ proc myOpen(module: PSym): PPassContext =
     c.importTable.addSym magicsys.systemModule # import the "System" identifier
     importAllSymbols(c, magicsys.systemModule)
   c.topLevelScope = openScope(c)
+  # don't be verbose unless the module belongs to the main package:
+  if module.owner.id == gMainPackageId:
+    gNotes = gMainPackageNotes
+  else:
+    if gMainPackageNotes == {}: gMainPackageNotes = gNotes
+    gNotes = ForeignPackageNotes
   result = c
 
 proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 17dd39595..65aa5fd58 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -129,7 +129,11 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   add(result, ')')
   var candidates = ""
   for err in errors:
-    add(candidates, err.getProcHeader(prefer))
+    if err.kind in routineKinds and err.ast != nil:
+      add(candidates, renderTree(err.ast,
+            {renderNoBody, renderNoComments,renderNoPragmas}))
+    else:
+      add(candidates, err.getProcHeader(prefer))
     add(candidates, "\n")
   if candidates != "":
     add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index a13f2c232..91a1ebf86 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -47,7 +47,7 @@ type
     efLValue, efWantIterator, efInTypeof,
     efWantStmt, efAllowStmt, efDetermineType,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
-    efNoProcvarCheck
+    efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo
   TExprFlags* = set[TExprFlag]
 
   TTypeAttachedOp* = enum
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 5aac1c2ac..57fb3ceed 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -15,7 +15,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   markUsed(n.info, s)
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
-  result = evalTemplate(n, s, getCurrOwner())
+  result = evalTemplate(n, s, getCurrOwner(), efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
   popInfoContext()
 
@@ -717,6 +717,34 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
       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 semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym;
+                       flags: TExprFlags): PNode =
+  # We received untransformed bracket expression coming from macroOrTmpl[].
+  # Transform it to macro or template call, where first come normal
+  # arguments, next come generic template arguments.
+  var sons = newSeq[PNode]()
+  sons.add inner.sons[0]
+  # Normal arguments:
+  for i in 1..<outer.len:
+    sons.add outer.sons[i]
+  # Generic template arguments from bracket expression:
+  for i in 1..<inner.len:
+    sons.add inner.sons[i]
+  shallowCopy(outer.sons, sons)
+  # FIXME: Shouldn't we check sfImmediate and call semDirectOp?
+  # However passing to semDirectOp doesn't work here.
+  case s.kind
+  of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
+  of skTemplate: result = semTemplateExpr(c, outer, s, flags)
+  else: assert(false)
+  return
+
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = nil
   checkMinSonsLen(n, 1)
@@ -732,10 +760,15 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
       return semExpr(c, result, flags)
   else:
-    n.sons[0] = semExpr(c, n.sons[0])
+    n.sons[0] = semExpr(c, n.sons[0], {efInCall})
     let t = n.sons[0].typ
     if t != nil and t.kind == tyVar:
       n.sons[0] = newDeref(n.sons[0])
+    elif n.sons[0].kind == nkBracketExpr:
+      let s = bracketedMacro(n.sons[0])
+      if s != nil:
+        return semBracketedMacro(c, n, n.sons[0], s, flags)
+
   let nOrig = n.copyTree
   semOpAux(c, n)
   var t: PType = nil
@@ -965,8 +998,20 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       else: result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
-  of skMacro: result = semMacroExpr(c, n, n, s, flags)
-  of skTemplate: result = semTemplateExpr(c, n, s, flags)
+  of skMacro:
+    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
+      markUsed(n.info, s)
+      styleCheckUse(n.info, s)
+      result = newSymNode(s, n.info)
+    else:
+      result = semMacroExpr(c, n, n, s, flags)
+  of skTemplate:
+    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
+      markUsed(n.info, s)
+      styleCheckUse(n.info, s)
+      result = newSymNode(s, n.info)
+    else:
+      result = semTemplateExpr(c, n, s, flags)
   of skParam:
     markUsed(n.info, s)
     styleCheckUse(n.info, s)
@@ -1193,7 +1238,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result.add(x[0])
     return
   checkMinSonsLen(n, 2)
-  n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck})
+  # make sure we don't evaluate generic macros/templates
+  n.sons[0] = semExprWithType(c, n.sons[0],
+                              {efNoProcvarCheck, efNoEvaluateGeneric})
   let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
   case arr.kind
   of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
@@ -1236,12 +1283,30 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     let s = if n.sons[0].kind == nkSym: n.sons[0].sym
             elif n[0].kind in nkSymChoices: n.sons[0][0].sym
             else: nil
-    if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
-      # type parameters: partial generic specialization
-      n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
-      result = explicitGenericInstantiation(c, n, s)
-    elif s != nil and s.kind == skType:
-      result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+    if s != nil:
+      case s.kind
+      of skProc, skMethod, skConverter, skIterator:
+        # type parameters: partial generic specialization
+        n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
+        result = explicitGenericInstantiation(c, n, s)
+      of skMacro, skTemplate:
+        if efInCall in flags:
+          # We are processing macroOrTmpl[] in macroOrTmpl[](...) call.
+          # Return as is, so it can be transformed into complete macro or
+          # template call in semIndirectOp caller.
+          result = n
+        else:
+          # We are processing macroOrTmpl[] not in call. Transform it to the
+          # macro or template call with generic arguments here.
+          n.kind = nkCall
+          case s.kind
+          of skMacro: result = semMacroExpr(c, n, n, s, flags)
+          of skTemplate: result = semTemplateExpr(c, n, s, flags)
+          else: discard
+      of skType:
+        result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+      else:
+        c.p.bracketExpr = n.sons[0]
     else:
       c.p.bracketExpr = n.sons[0]
 
@@ -1818,7 +1883,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   result = nil
 
   template setResult(e: expr) =
-    if semCheck: result = semStmt(c, e) # do not open a new scope!
+    if semCheck: result = semExpr(c, e) # do not open a new scope!
     else: result = e
 
   # Check if the node is "when nimvm"
@@ -1827,6 +1892,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # else:
   #   ...
   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]
@@ -1843,7 +1909,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       checkSonsLen(it, 2)
       if whenNimvm:
         if semCheck:
-          it.sons[1] = semStmt(c, it.sons[1])
+          it.sons[1] = semExpr(c, it.sons[1])
+          typ = commonType(typ, it.sons[1].typ)
         result = n # when nimvm is not elimited until codegen
       else:
         var e = semConstExpr(c, it.sons[0])
@@ -1857,12 +1924,14 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       checkSonsLen(it, 1)
       if result == nil or whenNimvm:
         if semCheck:
-          it.sons[0] = semStmt(c, it.sons[0])
+          it.sons[0] = semExpr(c, it.sons[0])
+          typ = commonType(typ, it.sons[0].typ)
         if result == nil:
           result = it.sons[0]
     else: illFormedAst(n)
   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.
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index c5a8cc2a2..feea6ce34 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -489,7 +489,7 @@ proc leValueConv(a, b: PNode): bool =
   of nkCharLit..nkUInt64Lit:
     case b.kind
     of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
-    of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal)
+    of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
     else: internalError(a.info, "leValueConv")
   of nkFloatLit..nkFloat128Lit:
     case b.kind
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 14631a590..e4ac56cd6 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -43,9 +43,11 @@ proc rawHandleSelf(c: PContext; owner: PSym) =
       if arg.name.id == c.selfName.id:
         c.p.selfSym = arg
         arg.flags.incl sfIsSelf
-        let t = c.p.selfSym.typ.skipTypes(abstractPtrs)
-        if t.kind == tyObject:
+        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(abstractPtrs)
 
 proc pushProcCon*(c: PContext; owner: PSym) =
   rawPushProcCon(c, owner)
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 150680af7..6cd5c4a3c 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -12,25 +12,26 @@
 
 import ast, astalgo, msgs, types
 
-proc ithField(n: PNode, field: int): PSym =
+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-i)
+      result = ithField(n.sons[i], field)
       if result != nil: return
   of nkRecCase:
     if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
-    result = ithField(n.sons[0], field-1)
+    result = ithField(n.sons[0], field)
     if result != nil: return
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
-        result = ithField(lastSon(n.sons[i]), field-1)
+        result = ithField(lastSon(n.sons[i]), field)
         if result != nil: return
       else: internalError(n.info, "ithField(record case branch)")
   of nkSym:
     if field == 0: result = n.sym
+    else: dec(field)
   else: discard
 
 proc annotateType*(n: PNode, t: PType) =
@@ -39,10 +40,13 @@ proc annotateType*(n: PNode, t: PType) =
   # to not to skip tyGenericInst
   case n.kind
   of nkObjConstr:
+    let x = t.skipTypes(abstractPtrs)
     n.typ = t
     for i in 1 .. <n.len:
-      let field = x.n.ithField(i - 1)
-      if field.isNil: globalError n.info, "invalid field at index " & $i
+      var j = i-1
+      let field = x.n.ithField(j)
+      if field.isNil:
+        globalError n.info, "invalid field at index " & $i
       else:
         internalAssert(n.sons[i].kind == nkExprColonExpr)
         annotateType(n.sons[i].sons[1], field.typ)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f6bfca955..0b2343c10 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -17,7 +17,8 @@ proc semDiscard(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 1)
   if n.sons[0].kind != nkEmpty:
     n.sons[0] = semExprWithType(c, n.sons[0])
-    if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard)
+    if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone:
+      localError(n.info, errInvalidDiscard)
 
 proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   result = n
@@ -535,7 +536,7 @@ proc semConst(c: PContext, n: PNode): PNode =
       localError(a.sons[2].info, errConstExprExpected)
       continue
     if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
-      localError(a.info, errXisNoType, typeToString(typ))
+      localError(a.info, "invalid type for const: " & typeToString(typ))
       continue
     v.typ = typ
     v.ast = def               # no need to copy
@@ -781,9 +782,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
       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}:
+      if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
+          s.typ.kind notin {tyObject, tyEnum}:
         # type aliases are hard:
-        #MessageOut('for type ' + typeToString(s.typ));
         var t = semTypeNode(c, x, nil)
         assert t != nil
         if t.kind in {tyObject, tyEnum, tyDistinct}:
@@ -929,6 +930,17 @@ proc semProcAnnotation(c: PContext, prc: PNode;
       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, ast.emptyNode, orig)
+  else:
+    n.sons[miscPos].sons[1] = orig
+  n.sons[genericParamsPos] = result
+
 proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # XXX semProcAux should be good enough for this now, we will eventually
   # remove semLambda
@@ -947,8 +959,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
-    n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
-    gp = n.sons[genericParamsPos]
+    gp = setGenericParamsMisc(c, n)
   else:
     gp = newNodeI(nkGenericParams, n.info)
 
@@ -1170,8 +1181,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
-    n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
-    gp = n.sons[genericParamsPos]
+    gp = setGenericParamsMisc(c, n)
   else:
     gp = newNodeI(nkGenericParams, n.info)
   # process parameters:
@@ -1250,7 +1260,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
 
       c.p.wasForwarded = proto != nil
       maybeAddResult(c, s, n)
-      if sfImportc notin s.flags:
+      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'
@@ -1330,7 +1340,8 @@ proc finishMethod(c: PContext, s: PSym) =
 proc semMethod(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
-
+  # macros can transform methods to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
   if not isGenericRoutine(s):
     # why check for the body? bug #2400 has none. Checking for sfForward makes
@@ -1345,6 +1356,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
   checkSonsLen(n, bodyPos + 1)
   result = semProcAux(c, n, skConverter, converterPragmas)
+  # macros can transform converters to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
   var t = s.typ
   if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter")
@@ -1354,6 +1367,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
 proc semMacroDef(c: PContext, n: PNode): PNode =
   checkSonsLen(n, bodyPos + 1)
   result = semProcAux(c, n, skMacro, macroPragmas)
+  # macros can transform macros to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
   var t = s.typ
   if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index ba17cc307..13b283fe5 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -398,15 +398,18 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym =
   # identifier with visibility
   if n.kind == nkPostfix:
-    if sonsLen(n) == 2 and n.sons[0].kind == nkIdent:
+    if sonsLen(n) == 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 = n.sons[0].ident
+      var v = considerQuotedIdent(n.sons[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
-        localError(n.sons[0].info, errInvalidVisibilityX, v.s)
+        if not (sfExported in allowed):
+          localError(n.sons[0].info, errXOnlyAtModuleScope, "export")
+        else:
+          localError(n.sons[0].info, errInvalidVisibilityX, renderTree(n[0]))
     else:
       illFormedAst(n)
   else:
@@ -1094,10 +1097,14 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
         result = instGenericContainer(c, n.info, result,
                                       allowMetaTypes = false)
 
-proc semTypeExpr(c: PContext, n: PNode): PType =
+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:
+    if prev != nil and prev.sym != nil and result.sym.isNil:
+      result.sym = prev.sym
+      result.sym.typ = result
   else:
     localError(n.info, errTypeExpected, n.renderTree)
     result = errorType(c)
@@ -1177,7 +1184,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         localError(n.info, errGenerated, "invalid type")
     elif n[0].kind notin nkIdentKinds:
-      result = semTypeExpr(c, n)
+      result = semTypeExpr(c, n, prev)
     else:
       let op = considerQuotedIdent(n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
@@ -1218,7 +1225,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
         result = typExpr.typ
       else:
-        result = semTypeExpr(c, n)
+        result = semTypeExpr(c, n, prev)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ecef11d13..e72db45e7 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -242,6 +242,8 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
     for i in 1 .. <arg.len:
       result.add(" | ")
       result.add typeToString(arg[i].typ, prefer)
+  elif arg.typ == nil:
+    result = "void"
   else:
     result = arg.typ.typeToString(prefer)
 
@@ -253,15 +255,15 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
     if n.sons[i].kind == nkExprEqExpr:
       add(result, renderTree(n.sons[i].sons[0]))
       add(result, ": ")
-      if arg.typ.isNil:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
         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:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
         arg = c.semOperand(c, n.sons[i])
         n.sons[i] = arg
-    if arg.typ.kind == tyError: return
+    if arg.typ != nil and arg.typ.kind == tyError: return
     add(result, argTypeToString(arg, prefer))
     if i != sonsLen(n) - 1: add(result, ", ")
 
diff --git a/compiler/vm.nim b/compiler/vm.nim
index f799334d6..f275b7b9b 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1185,19 +1185,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetType:
       let rb = instr.regB
       let rc = instr.regC
-      if rc == 0:
+      case rc:
+      of 0:
+        # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, errGenerated, "node has no type")
-      else:
+      of 1:
         # typeKind opcode:
         ensureKind(rkInt)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].intVal = ord(regs[rb].node.typ.kind)
         #else:
         #  stackTrace(c, tos, pc, errGenerated, "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(regs[rb].node.typ, c.debug[pc])
+        else:
+          stackTrace(c, tos, pc, errGenerated, "node has no type")
+      else:
+        # getTypeImpl opcode:
+        ensureKind(rkNode)
+        if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+          regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc])
+        else:
+          stackTrace(c, tos, pc, errGenerated, "node has no type")
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index a4f02092d..678a765f4 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, osproc, streams, options, idents, securehash
+import ast, types, msgs, os, osproc, streams, options, idents, securehash
 
 proc readOutput(p: Process): string =
   result = ""
@@ -51,7 +51,9 @@ proc opGorge*(cmd, input, cache: string): string =
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
   try:
-    let filename = file.findFile
+    var filename = parentDir(info.toFullPath) / file
+    if not fileExists(filename):
+      filename = file.findFile
     result = readFile(filename)
     # we produce a fake include statement for every slurped filename, so that
     # the module dependencies are accurate:
@@ -67,9 +69,11 @@ proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
   result = newSymNode(sym)
   result.typ = t
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode
 
-proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
+proc mapTypeToBracketX(name: string; t: PType; info: TLineInfo;
+                       inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
   result.add atomicTypeX(name, t, info)
   for i in 0 .. < t.len:
@@ -78,10 +82,39 @@ proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
       void.typ = newType(tyEmpty, t.owner)
       result.add void
     else:
-      result.add mapTypeToAst(t.sons[i], info)
+      result.add mapTypeToAstX(t.sons[i], info, inst)
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode =
+  var allowRecursion = allowRecursionX
   template atomicType(name): expr = atomicTypeX(name, t, info)
+  template mapTypeToAst(t,info): expr = mapTypeToAstX(t, info, inst)
+  template mapTypeToAstR(t,info): expr = mapTypeToAstX(t, info, inst, true)
+  template mapTypeToAst(t,i,info): expr =
+    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
+    else: ast.emptyNode
+  template mapTypeToBracket(name,t,info): expr =
+    mapTypeToBracketX(name, t, info, inst)
+  template newNodeX(kind):expr =
+    newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
+  template newIdent(s):expr =
+    var r = newNodeX(nkIdent)
+    r.add !s
+    r
+  template newIdentDefs(n,t):expr =
+    var id = newNodeX(nkIdentDefs)
+    id.add n  # name
+    id.add mapTypeToAst(t, info)  # type
+    id.add ast.emptyNode  # no assigned value
+    id
+  template newIdentDefs(s):expr = newIdentDefs(s, s.typ)
+
+  if inst:
+    if t.sym != nil:  # if this node has a symbol
+      if allowRecursion:  # getTypeImpl behavior: turn off recursion
+        allowRecursion = false
+      else:  # getTypeInst behavior: return symbol
+        return atomicType(t.sym.name.s)
 
   case t.kind
   of tyNone: result = atomicType("none")
@@ -94,7 +127,14 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
   of tyArrayConstr, tyArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("array")
-    result.add mapTypeToAst(t.sons[0], info)
+    if inst and t.sons[0].kind == tyRange:
+      var rng = newNodeX(nkInfix)
+      rng.add newIdentNode(getIdent(".."), info)
+      rng.add t.sons[0].n.sons[0].copyTree
+      rng.add t.sons[0].n.sons[1].copyTree
+      result.add rng
+    else:
+      result.add mapTypeToAst(t.sons[0], info)
     result.add mapTypeToAst(t.sons[1], info)
   of tyTypeDesc:
     if t.base != nil:
@@ -107,34 +147,95 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
     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)
-  of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
+  of tyGenericInst:
+    if inst:
+      if allowRecursion:
+        result = mapTypeToAstR(t.lastSon, info)
+      else:
+        result = newNodeX(nkBracketExpr)
+        result.add mapTypeToAst(t.lastSon, info)
+        for i in 1 .. < t.len-1:
+          result.add mapTypeToAst(t.sons[i], info)
+    else:
+      result = mapTypeToAst(t.lastSon, info)
+  of tyGenericBody, tyOrdinal, tyUserTypeClassInst:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
-    if allowRecursion:
-      result = mapTypeToBracket("distinct", t, info)
+    if inst:
+      result = newNodeX(nkDistinctTy)
+      result.add mapTypeToAst(t.sons[0], info)
     else:
-      result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym==nil:
+        result = mapTypeToBracket("distinct", t, info)
+      else:
+        result = atomicType(t.sym.name.s)
   of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
   of tyObject:
-    if allowRecursion:
-      result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-      if t.sons[0] == nil:
-        result.add ast.emptyNode
+    if inst:
+      result = newNodeX(nkObjectTy)
+      result.add ast.emptyNode  # pragmas not reconstructed yet
+      if t.sons[0]==nil: result.add ast.emptyNode  # handle parent object
+      else:
+        var nn = newNodeX(nkOfInherit)
+        nn.add mapTypeToAst(t.sons[0], info)
+        result.add nn
+      if t.n.sons.len>0:
+        var rl = copyNode(t.n)  # handle nkRecList
+        for s in t.n.sons:
+          rl.add newIdentDefs(s)
+        result.add rl
       else:
-        result.add mapTypeToAst(t.sons[0], info)
-      result.add copyTree(t.n)
+        result.add ast.emptyNode
     else:
-      result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym == nil:
+        result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
+        result.add ast.emptyNode
+        if t.sons[0] == nil:
+          result.add ast.emptyNode
+        else:
+          result.add mapTypeToAst(t.sons[0], info)
+        result.add copyTree(t.n)
+      else:
+        result = atomicType(t.sym.name.s)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
     result.add copyTree(t.n)
-  of tyTuple: result = mapTypeToBracket("tuple", t, info)
+  of tyTuple:
+    if inst:
+      result = newNodeX(nkTupleTy)
+      for s in t.n.sons:
+        result.add newIdentDefs(s)
+    else:
+      result = mapTypeToBracket("tuple", t, info)
   of tySet: result = mapTypeToBracket("set", t, info)
-  of tyPtr: result = mapTypeToBracket("ptr", t, info)
-  of tyRef: result = mapTypeToBracket("ref", t, info)
+  of tyPtr:
+    if inst:
+      result = newNodeX(nkPtrTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ptr", t, info)
+  of tyRef:
+    if inst:
+      result = newNodeX(nkRefTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ref", t, info)
   of tyVar: result = mapTypeToBracket("var", t, info)
   of tySequence: result = mapTypeToBracket("seq", t, info)
-  of tyProc: result = mapTypeToBracket("proc", t, info)
+  of tyProc:
+    if inst:
+      result = newNodeX(nkProcTy)
+      var fp = newNodeX(nkFormalParams)
+      if t.sons[0] == nil:
+        fp.add ast.emptyNode
+      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])
+      result.add fp
+      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+    else:
+      result = mapTypeToBracket("proc", t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
   of tyRange:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
@@ -174,10 +275,24 @@ proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
   of tyNot: result = mapTypeToBracket("not", t, info)
   of tyAnything: result = atomicType"anything"
   of tyStatic, tyFromExpr, tyFieldAccessor:
-    result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    result.add atomicType("static")
-    if t.n != nil:
-      result.add t.n.copyTree
+    if inst:
+      if t.n != nil: result = t.n.copyTree
+      else: result = atomicType "void"
+    else:
+      result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
+      result.add atomicType "static"
+      if t.n != nil:
+        result.add t.n.copyTree
 
 proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAst(t, info, true)
+  result = mapTypeToAstX(t, info, false, 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*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, 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*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index bd32ccc17..675fb2dc2 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -983,7 +983,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNGetType:
     let tmp = c.genx(n.sons[1])
     if dest < 0: dest = c.getTemp(n.typ)
-    c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
+    let rc = case n[0].sym.name.s:
+      of "getType": 0
+      of "typeKind": 1
+      of "getTypeInst": 2
+      else: 3  # "getTypeImpl"
+    c.gABC(n, opcNGetType, dest, tmp, rc)
     c.freeTemp(tmp)
     #genUnaryABC(c, n, dest, opcNGetType)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
@@ -1882,8 +1887,8 @@ proc optimizeJumps(c: PCtx; start: int) =
     else: discard
 
 proc genProc(c: PCtx; s: PSym): int =
-  let x = s.ast.sons[optimizedCodePos]
-  if x.kind == nkEmpty:
+  var x = s.ast.sons[miscPos]
+  if x.kind == nkEmpty or x[0].kind == nkEmpty:
     #if s.name.s == "outterMacro" or s.name.s == "innerProc":
     #  echo "GENERATING CODE FOR ", s.name.s
     let last = c.code.len-1
@@ -1894,7 +1899,11 @@ proc genProc(c: PCtx; s: PSym): int =
       c.debug.setLen(last)
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
-    s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result)
+    if x.kind == nkEmpty:
+      x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode)
+    else:
+      x.sons[0] = newIntNode(nkIntLit, result)
+    s.ast.sons[miscPos] = x
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
     let body = s.getBody
@@ -1929,4 +1938,4 @@ proc genProc(c: PCtx; s: PSym): int =
     c.prc = oldPrc
   else:
     c.prc.maxSlots = s.offset
-    result = x.intVal.int
+    result = x[0].intVal.int
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index e40e05eff..d0b3119e2 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -24,22 +24,27 @@ template osop(op) {.immediate, dirty.} =
 template systemop(op) {.immediate, dirty.} =
   registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
 
-template wrap1f(op) {.immediate, dirty.} =
+template wrap1f_math(op) {.immediate, dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getFloat(a, 0)))
   mathop op
 
-template wrap2f(op) {.immediate, dirty.} =
+template wrap2f_math(op) {.immediate, dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
   mathop op
 
-template wrap1s(op) {.immediate, dirty.} =
+template wrap1s_os(op) {.immediate, dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getString(a, 0)))
   osop op
 
-template wrap2svoid(op) {.immediate, dirty.} =
+template wrap1s_system(op) {.immediate, dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0)))
+  systemop op
+
+template wrap2svoid_system(op) {.immediate, dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     op(getString(a, 0), getString(a, 1))
   systemop op
@@ -55,34 +60,35 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode =
                               newStrNode(nkStrLit, f))
 
 proc registerAdditionalOps*(c: PCtx) =
-  wrap1f(sqrt)
-  wrap1f(ln)
-  wrap1f(log10)
-  wrap1f(log2)
-  wrap1f(exp)
-  wrap1f(round)
-  wrap1f(arccos)
-  wrap1f(arcsin)
-  wrap1f(arctan)
-  wrap2f(arctan2)
-  wrap1f(cos)
-  wrap1f(cosh)
-  wrap2f(hypot)
-  wrap1f(sinh)
-  wrap1f(sin)
-  wrap1f(tan)
-  wrap1f(tanh)
-  wrap2f(pow)
-  wrap1f(trunc)
-  wrap1f(floor)
-  wrap1f(ceil)
-  wrap2f(fmod)
+  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)
 
-  wrap1s(getEnv)
-  wrap1s(existsEnv)
-  wrap1s(dirExists)
-  wrap1s(fileExists)
-  wrap2svoid(writeFile)
+  wrap1s_os(getEnv)
+  wrap1s_os(existsEnv)
+  wrap1s_os(dirExists)
+  wrap1s_os(fileExists)
+  wrap2svoid_system(writeFile)
+  wrap1s_system(readFile)
   systemop getCurrentExceptionMsg
   registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
     setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
diff --git a/doc/filters.txt b/doc/filters.txt
index 46bc6c3e5..1937b187c 100644
--- a/doc/filters.txt
+++ b/doc/filters.txt
@@ -23,6 +23,10 @@ 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
 =============
@@ -41,9 +45,6 @@ Filters can be combined with the ``|`` pipe operator::
 Available filters
 =================
 
-**Hint:** With ``--verbosity:2`` (or higher) Nim lists the processed code
-after each filter application.
-
 Replace filter
 --------------
 
diff --git a/doc/lib.txt b/doc/lib.txt
index a789c6537..f749763a5 100644
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -202,6 +202,9 @@ Math libraries
 * `mersenne <mersenne.html>`_
   Mersenne twister random number generator.
 
+* `random <random.html>`_
+  Fast and tiny random number generator.
+
 * `stats <stats.html>`_
   Statistical analysis
 
diff --git a/doc/manual/exceptions.txt b/doc/manual/exceptions.txt
index e7af65386..d06c13df4 100644
--- a/doc/manual/exceptions.txt
+++ b/doc/manual/exceptions.txt
@@ -155,4 +155,4 @@ Exception hierarchy
 
 The exception tree is defined in the `system <system.html>`_ module:
 
-.. include:: exception_hierarchy_fragment.txt
+.. include:: ../exception_hierarchy_fragment.txt
diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt
index 4187a60a4..4d03023c3 100644
--- a/doc/manual/lexing.txt
+++ b/doc/manual/lexing.txt
@@ -116,7 +116,7 @@ operator characters instead.
 The following keywords are reserved and cannot be used as identifiers:
 
 .. code-block:: nim
-   :file: keywords.txt
+   :file: ../keywords.txt
 
 Some keywords are unused; they are reserved for future developments of the
 language.
diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt
index ca3b582ca..4f7483be8 100644
--- a/doc/manual/syntax.txt
+++ b/doc/manual/syntax.txt
@@ -119,6 +119,6 @@ Grammar
 
 The grammar's start symbol is ``module``.
 
-.. include:: grammar.txt
+.. include:: ../grammar.txt
    :literal:
 
diff --git a/doc/manual/types.txt b/doc/manual/types.txt
index a1596bcea..4c015ffb7 100644
--- a/doc/manual/types.txt
+++ b/doc/manual/types.txt
@@ -694,7 +694,7 @@ branch switch ``system.reset`` has to be used.
 Set type
 --------
 
-.. include:: sets_fragment.txt
+.. include:: ../sets_fragment.txt
 
 Reference and pointer types
 ---------------------------
diff --git a/doc/niminst.txt b/doc/niminst.txt
index 7bd0f719e..bf5cb0f50 100644
--- a/doc/niminst.txt
+++ b/doc/niminst.txt
@@ -27,7 +27,7 @@ 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:: doc/mytest.cfg
+.. include:: mytest.cfg
      :literal:
 
 The value of a key-value pair can reference user-defined variables via
@@ -190,6 +190,6 @@ 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
+.. include:: ../compiler/installer.ini
      :literal:
 
diff --git a/examples/unix_socket/client.nim b/examples/unix_socket/client.nim
new file mode 100644
index 000000000..f4283d64d
--- /dev/null
+++ b/examples/unix_socket/client.nim
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 000000000..e798bbb48
--- /dev/null
+++ b/examples/unix_socket/server.nim
@@ -0,0 +1,14 @@
+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/koch.nim b/koch.nim
index f7c0ae204..e8b08c5d2 100644
--- a/koch.nim
+++ b/koch.nim
@@ -336,10 +336,11 @@ template `|`(a, b): expr = (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 :-)
-  exec "nim cc --taintMode:on tests/testament/tester"
+  let nimexe = findNim()
+  exec nimexe & " cc --taintMode:on 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!
-  exec "nim c --lib:lib -d:release --opt:speed compiler/nim.nim"
+  exec nimexe & " 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"):
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 2e3596d0f..16a954611 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -12,7 +12,7 @@ include "system/inclrtl"
 ## This module contains the interface to the compiler's abstract syntax
 ## tree (`AST`:idx:). Macros operate on this tree.
 
-## .. include:: ../doc/astspec.txt
+## .. include:: ../../doc/astspec.txt
 
 type
   NimNodeKind* = enum
@@ -197,6 +197,18 @@ 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`.
 
+proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for a specific instance
+
+proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for a specific instance
+
+proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for the implementation
+
+proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
+  ## Like getType except it includes generic parameters for the implementation
+
 proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
 
 proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
index 3a14e6304..d6343acc7 100644
--- a/lib/impure/db_odbc.nim
+++ b/lib/impure/db_odbc.nim
@@ -210,7 +210,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
       add(result, c)
 
 proc prepareFetch(db: var DbConn, query: SqlQuery,
-                args: varargs[string, `$`]) : TSqlSmallInt {.
+                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
@@ -222,9 +222,8 @@ proc prepareFetch(db: var DbConn, query: SqlQuery,
   var q = dbFormat(query, args)
   db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
   db.sqlCheck(SQLExecute(db.stmt))
-  var retcode = SQLFetch(db.stmt)
-  db.sqlCheck(retcode)
-  result=retcode
+  result = SQLFetch(db.stmt)
+  db.sqlCheck(result)
 
 proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
                 args: varargs[string, `$`]) {.
@@ -250,8 +249,8 @@ proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool
     var
       rCnt = -1
     res = SQLRowCount(db.stmt, rCnt)
-    if res != SQL_SUCCESS: dbError(db)
     properFreeResult(SQL_HANDLE_STMT, db.stmt)
+    if res != SQL_SUCCESS: dbError(db)
   except: discard
   return res == SQL_SUCCESS
 
@@ -286,10 +285,10 @@ iterator fastRows*(db: var DbConn, query: SqlQuery,
     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, 
+    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. 
+    # 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:
@@ -308,8 +307,8 @@ iterator fastRows*(db: var DbConn, query: SqlQuery,
         cCnt = tempcCnt
       yield rowRes
       res = SQLFetch(db.stmt)
-  db.sqlCheck(res)
   properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  db.sqlCheck(res)
 
 iterator instantRows*(db: var DbConn, query: SqlQuery,
                       args: varargs[string, `$`]): InstantRow
@@ -317,14 +316,14 @@ iterator instantRows*(db: var DbConn, query: SqlQuery,
   ## Same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within the interator body.
   var
-    rowRes: 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, 
+    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. 
+    # 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:
@@ -343,8 +342,8 @@ iterator instantRows*(db: var DbConn, query: SqlQuery,
         cCnt = tempcCnt
       yield (row: rowRes, len: cCnt.int)
       res = SQLFetch(db.stmt)
-  db.sqlCheck(res)
   properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  db.sqlCheck(res)
 
 proc `[]`*(row: InstantRow, col: int): string {.inline.} =
   ## Returns text for given column of the row
@@ -364,10 +363,10 @@ proc getRow*(db: var DbConn, query: SqlQuery,
     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, 
+    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. 
+    ## 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:
@@ -385,8 +384,8 @@ proc getRow*(db: var DbConn, query: SqlQuery,
       cCnt = tempcCnt
     res = SQLFetch(db.stmt)
     result = rowRes
-  db.sqlCheck(res)
   properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  db.sqlCheck(res)
 
 proc getAllRows*(db: var DbConn, query: SqlQuery,
                  args: varargs[string, `$`]): seq[Row] {.
@@ -398,10 +397,10 @@ proc getAllRows*(db: var DbConn, query: SqlQuery,
     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, 
+    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. 
+    ## 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:
@@ -421,8 +420,8 @@ proc getAllRows*(db: var DbConn, query: SqlQuery,
       rows.add(rowRes)
       res = SQLFetch(db.stmt)
     result = rows
-    db.sqlCheck(res)
   properFreeResult(SQL_HANDLE_STMT, db.stmt)
+  db.sqlCheck(res)
 
 iterator rows*(db: var DbConn, query: SqlQuery,
                args: varargs[string, `$`]): Row {.
@@ -544,4 +543,4 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {.
   ## 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")
\ No newline at end of file
+  dbError("setEncoding() is currently not implemented by the db_odbc module")
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 1c2a39854..c636c5203 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -15,6 +15,8 @@ from math import ceil
 import options
 from unicode import runeLenAt
 
+export options
+
 
 ## What is NRE?
 ## ============
@@ -555,6 +557,16 @@ proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[st
   for match in str.findIter(pattern, start, endpos):
     result.add(match.match)
 
+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))``.
+  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:
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 60bb6c77f..d49c6d1c1 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -22,7 +22,7 @@
 ## though.
 ## PRCE's licence follows:
 ##
-## .. include:: ../doc/regexprs.txt
+## .. include:: ../../doc/regexprs.txt
 ##
 
 import
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index e1d5f902e..ebe6fa19b 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -323,6 +323,11 @@ proc newSharedState(options: RstParseOptions,
   result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
   result.findFile = if not isNil(findFile): findFile else: defaultFindFile
 
+proc findRelativeFile(p: RstParser; filename: string): string =
+  result = p.filename.splitFile.dir / filename
+  if not existsFile(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)
@@ -1500,7 +1505,7 @@ proc dirInclude(p: var RstParser): PRstNode =
   result = nil
   var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil)
   var filename = strip(addNodes(n.sons[0]))
-  var path = p.s.findFile(filename)
+  var path = p.findRelativeFile(filename)
   if path == "":
     rstMessage(p, meCannotOpenFile, filename)
   else:
@@ -1511,7 +1516,7 @@ proc dirInclude(p: var RstParser): PRstNode =
     else:
       var q: RstParser
       initParser(q, p.s)
-      q.filename = filename
+      q.filename = path
       q.col += getTokens(readFile(path), false, q.tok)
       # workaround a GCC bug; more like the interior pointer bug?
       #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0:
@@ -1538,7 +1543,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
   result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
   var filename = strip(getFieldValue(result, "file"))
   if filename != "":
-    var path = p.s.findFile(filename)
+    var path = p.findRelativeFile(filename)
     if path == "": rstMessage(p, meCannotOpenFile, filename)
     var n = newRstNode(rnLiteralBlock)
     add(n, newRstNode(rnLeaf, readFile(path)))
@@ -1590,7 +1595,7 @@ proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
                contentParser: SectionParser) =
   var filename = getFieldValue(result, "file")
   if filename.len > 0:
-    var path = p.s.findFile(filename)
+    var path = p.findRelativeFile(filename)
     if path.len == 0:
       rstMessage(p, meCannotOpenFile, filename)
     else:
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 19b068b60..bb42d6902 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -448,6 +448,14 @@ when hasSpawnH:
     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
+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>".} = cint
@@ -457,6 +465,11 @@ type
     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
@@ -1613,6 +1626,16 @@ else:
   var
     MAP_POPULATE*: cint = 0
 
+when defined(linux) or defined(nimdoc):
+  when defined(alpha) or defined(mips) or defined(parisc) or
+      defined(sparc) 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
@@ -1621,6 +1644,10 @@ when defined(macosx):
     MSG_NOSIGNAL* = 0'i32
   var
     SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint
+elif defined(solaris):
+  # Solaris dont have MSG_NOSIGNAL
+  const
+    MSG_NOSIGNAL* = 0'i32
 else:
   var
     MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
@@ -2628,16 +2655,16 @@ proc utimes*(path: cstring, times: ptr array [2, Timeval]): int {.
   ##
   ## For more information read http://www.unix.com/man-page/posix/3/utimes/.
 
-proc handle_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.importc: "signal", header: "<signal.h>".} 
- 
-template onSignal*(signals: varargs[cint], body: untyped): stmt = 
-  ## Setup code to be executed when Unix signals are received. Example: 
-  ## from posix import SIGINT, SIGTERM 
-  ## onSignal(SIGINT, SIGTERM): 
-  ##   echo "bye" 
- 
-  for s in signals: 
-    handle_signal(s, 
-      proc (sig: cint) {.noconv.} = 
-        body 
+proc handle_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.importc: "signal", header: "<signal.h>".}
+
+template onSignal*(signals: varargs[cint], body: untyped): stmt =
+  ## Setup code to be executed when Unix signals are received. Example:
+  ## from posix import SIGINT, SIGTERM
+  ## onSignal(SIGINT, SIGTERM):
+  ##   echo "bye"
+
+  for s in signals:
+    handle_signal(s,
+      proc (sig: cint) {.noconv.} =
+        body
     )
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 8345d43e5..2c7aaf2bf 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -11,7 +11,7 @@ include "system/inclrtl"
 
 import os, oids, tables, strutils, macros, times, heapqueue
 
-import nativesockets, net
+import nativesockets, net, queues
 
 export Port, SocketFlag
 
@@ -155,6 +155,9 @@ type
 
 when not defined(release):
   var currentID = 0
+
+proc callSoon*(cbproc: proc ()) {.gcsafe.}
+
 proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
   ## Creates a new future.
   ##
@@ -257,7 +260,7 @@ proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
   ## passes ``future`` as a param to the callback.
   future.cb = cb
   if future.finished:
-    future.cb()
+    callSoon(future.cb)
 
 proc `callback=`*[T](future: Future[T],
     cb: proc (future: Future[T]) {.closure,gcsafe.}) =
@@ -352,14 +355,42 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   fut2.callback = cb
   return retFuture
 
+proc all*[A](futs: seq[Future[A]]): Future[seq[A]] =
+  ## Returns a future which will complete once all futures in ``futs``
+  ## complete.
+  ##
+  ## The resulting future will hold the values of all awaited futures,
+  ## in the order they are passed.
+
+  var
+    retFuture = newFuture[seq[A]]("asyncdispatch.all")
+    retValues = newSeq[A](len(futs))
+    completedFutures = 0
+
+  for i, fut in futs:
+    fut.callback = proc(f: Future[A]) =
+      retValues[i] = f.read()
+      inc(completedFutures)
+
+      if completedFutures == len(futs):
+        retFuture.complete(retValues)
+
+  return retFuture
+
 type
   PDispatcherBase = ref object of RootRef
     timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
+    callbacks: Queue[proc ()]
 
 proc processTimers(p: PDispatcherBase) {.inline.} =
   while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
 
+proc processPendingCallbacks(p: PDispatcherBase) =
+  while p.callbacks.len > 0:
+    var cb = p.callbacks.dequeue()
+    cb()
+
 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.
@@ -403,6 +434,7 @@ when defined(windows) or defined(nimdoc):
     result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
     result.handles = initSet[AsyncFD]()
     result.timers.newHeapQueue()
+    result.callbacks = initQueue[proc ()](64)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
   proc getGlobalDispatcher*(): PDispatcher =
@@ -429,7 +461,7 @@ when defined(windows) or defined(nimdoc):
   proc poll*(timeout = 500) =
     ## Waits for completion events and processes them.
     let p = getGlobalDispatcher()
-    if p.handles.len == 0 and p.timers.len == 0:
+    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.")
 
@@ -469,6 +501,8 @@ when defined(windows) or defined(nimdoc):
 
     # Timer processing.
     processTimers(p)
+    # Callback queue processing
+    processPendingCallbacks(p)
 
   var connectExPtr: pointer = nil
   var acceptExPtr: pointer = nil
@@ -930,6 +964,7 @@ else:
     new result
     result.selector = newSelector()
     result.timers.newHeapQueue()
+    result.callbacks = initQueue[proc ()](64)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
   proc getGlobalDispatcher*(): PDispatcher =
@@ -1025,7 +1060,10 @@ else:
         # (e.g. socket disconnected).
         discard
 
+    # Timer processing.
     processTimers(p)
+    # Callback queue processing
+    processPendingCallbacks(p)
 
   proc connect*(socket: AsyncFD, address: string, port: Port,
     domain = AF_INET): Future[void] =
@@ -1604,6 +1642,11 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
       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.
+  getGlobalDispatcher().callbacks.enqueue(cbproc)
+
 proc runForever*() =
   ## Begins a never ending global dispatcher poll loop.
   while true:
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 590b52c1a..1eac4fc00 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -39,6 +39,7 @@ type
   AsyncHttpServer* = ref object
     socket: AsyncSocket
     reuseAddr: bool
+    reusePort: bool
 
   HttpCode* = enum
     Http100 = "100 Continue",
@@ -99,10 +100,11 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
     of HttpVer10: 0
   result = protocol.major == major and protocol.minor == minor
 
-proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer =
+proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer =
   ## Creates a new ``AsyncHttpServer`` instance.
   new result
   result.reuseAddr = reuseAddr
+  result.reusePort = reusePort
 
 proc addHeaders(msg: var string, headers: StringTableRef) =
   for k, v in headers:
@@ -217,20 +219,20 @@ proc processClient(client: AsyncSocket, address: string,
         else:
           await client.sendStatus("417 Expectation Failed")
 
-      # Read the body
-      # - Check for Content-length header
-      if request.headers.hasKey("Content-Length"):
-        var contentLength = 0
-        if parseInt(request.headers.getOrDefault("Content-Length"),
-                    contentLength) == 0:
-          await request.respond(Http400, "Bad Request. Invalid Content-Length.")
-          continue
-        else:
-          request.body = await client.recv(contentLength)
-          assert request.body.len == contentLength
-      else:
-        await request.respond(Http400, "Bad Request. No Content-Length.")
+    # Read the body
+    # - Check for Content-length header
+    if request.headers.hasKey("Content-Length"):
+      var contentLength = 0
+      if parseInt(request.headers.getOrDefault("Content-Length"),
+                  contentLength) == 0:
+        await request.respond(Http400, "Bad Request. Invalid Content-Length.")
         continue
+      else:
+        request.body = await client.recv(contentLength)
+        assert request.body.len == contentLength
+    elif request.reqMethod == "post":
+      await request.respond(Http400, "Bad Request. No Content-Length.")
+      continue
 
     case request.reqMethod
     of "get", "post", "head", "put", "delete", "trace", "options",
@@ -264,6 +266,8 @@ proc serve*(server: AsyncHttpServer, port: Port,
   server.socket = newAsyncSocket()
   if server.reuseAddr:
     server.socket.setSockOpt(OptReuseAddr, true)
+  if server.reusePort:
+    server.socket.setSockOpt(OptReusePort, true)
   server.socket.bindAddr(port, address)
   server.socket.listen()
 
diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim
index bb77331df..954d62491 100644
--- a/lib/pure/collections/LockFreeHash.nim
+++ b/lib/pure/collections/LockFreeHash.nim
@@ -52,7 +52,7 @@ when sizeof(int) == 4: # 32bit
   {.deprecated: [TRaw: Raw].}
 elif sizeof(int) == 8: # 64bit
   type
-    Raw = range[0..4611686018427387903]
+    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].}
@@ -244,9 +244,9 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
     echo("oldVal is Tomb!!!, should not happen")
   if pop(oldVal) != 0:
     result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
-  if result:
+  #if result:
     #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
-  else:
+  #else:
     #echo("copy slot failed")
   # Our copy is done so we disable the old slot
   while not ok:
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 0e3824a81..0817b38a3 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -657,7 +657,7 @@ template newSeqWith*(len: int, init: expr): expr =
   ##   seq2D[1][0] = true
   ##   seq2D[0][1] = true
   ##
-  ##   import math
+  ##   import random
   ##   var seqRand = newSeqWith(20, random(10))
   ##   echo seqRand
   var result {.gensym.} = newSeq[type(init)](len)
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 20e1bb7a9..17600b272 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -45,6 +45,59 @@ template withLock(t, x: untyped) =
   x
   release(t.lock)
 
+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
+  ##
+  acquire(t.lock)
+  try:
+    var hc: Hash
+    var index = rawGet(t, key, hc)
+    let hasKey = index >= 0
+    if hasKey:
+      var value {.inject.} = addr(t.data[index].val)
+      body
+  finally:
+    release(t.lock)
+
+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")
+  ##
+  acquire(t.lock)
+  try:
+    var hc: Hash
+    var index = rawGet(t, key, hc)
+    let hasKey = index >= 0
+    if hasKey:
+      var value {.inject.} = addr(t.data[index].val)
+      body1
+    else:
+      body2
+  finally:
+    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.
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 58a789e76..b702f6248 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -136,6 +136,51 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} =
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key)
 
+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.
+  ##
+  ## .. code-block:: nim
+  ##
+  ##   sharedTable.withValue(key, value) do:
+  ##     # block is executed only if ``key`` in ``t``
+  ##     value.name = "username" 
+  ##     value.uid = 1000
+  ##
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  let hasKey = index >= 0
+  if hasKey:
+    var value {.inject.} = addr(t.data[index].val)
+    body
+
+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")
+  ##
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  let hasKey = index >= 0
+  if hasKey:
+    var value {.inject.} = addr(t.data[index].val)
+    body1
+  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 = hash(key) and high(t.data)
diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim
index 22598b5c9..b0fd002ed 100644
--- a/lib/pure/concurrency/cpuload.nim
+++ b/lib/pure/concurrency/cpuload.nim
@@ -79,6 +79,8 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
   inc s.calls
 
 when not defined(testing) and isMainModule:
+  import random
+
   proc busyLoop() =
     while true:
       discard random(80)
diff --git a/lib/pure/future.nim b/lib/pure/future.nim
index 3793edc8b..4e2c1d893 100644
--- a/lib/pure/future.nim
+++ b/lib/pure/future.nim
@@ -29,21 +29,17 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
       of nnkExprColonExpr:
         identDefs.add ident[0]
         identDefs.add ident[1]
-      of nnkIdent:
+      else:
         identDefs.add newIdentNode("i" & $i)
         identDefs.add(ident)
-      else:
-        error("Incorrect type list in proc type declaration.")
       identDefs.add newEmptyNode()
       formalParams.add identDefs
-  of nnkIdent:
+  else:
     var identDefs = newNimNode(nnkIdentDefs)
     identDefs.add newIdentNode("i0")
     identDefs.add(p)
     identDefs.add newEmptyNode()
     formalParams.add identDefs
-  else:
-    error("Incorrect type list in proc type declaration.")
 
   result.add formalParams
   result.add newEmptyNode()
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index f473e47d1..d59b8ecfe 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -79,10 +79,13 @@
 ## constructor should be used for this purpose. However,
 ## currently only basic authentication is supported.
 
-import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math
+import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
+  math, random
 import asyncnet, asyncdispatch
 import nativesockets
 
+export strtabs
+
 type
   Response* = tuple[
     version: string,
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index b9da8a0dd..cea7a1e90 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -49,6 +49,10 @@
 ##         "age": herAge
 ##       }
 ##     ]
+##
+##    var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
+##    j2["details"] = %* {"age":35, "pi":3.1415}
+##    echo j2
 
 import
   hashes, tables, strutils, lexbase, streams, unicode, macros
@@ -707,17 +711,27 @@ proc `%`*(b: bool): JsonNode =
 
 proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
-  new(result)
-  result.kind = JObject
-  result.fields = initTable[string, JsonNode](4)
+  result = newJObject()
   for key, val in items(keyVals): result.fields[key] = val
 
-proc `%`*(elements: openArray[JsonNode]): JsonNode =
+template `%`*(j: JsonNode): JsonNode = j
+
+proc `%`*[T](elements: openArray[T]): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JArray JsonNode`
-  new(result)
-  result.kind = JArray
-  newSeq(result.elems, elements.len)
-  for i, p in pairs(elements): result.elems[i] = p
+  result = newJArray()
+  for elem in elements: result.add(%elem)
+
+proc `%`*(o: object): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
+  result = newJObject()
+  for k, v in o.fieldPairs: result[k] = %v
+
+proc `%`*(o: ref object): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
+  if o.isNil:
+    result = newJNull()
+  else:
+    result = %(o[])
 
 proc toJson(x: NimNode): NimNode {.compiletime.} =
   case x.kind
@@ -736,6 +750,9 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
     result = newNimNode(nnkTableConstr)
     x.expectLen(0)
 
+  of nnkNilLit:
+    result = newCall("newJNull")
+
   else:
     result = x
 
@@ -992,7 +1009,7 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
     result.add("null")
 
 proc pretty*(node: JsonNode, indent = 2): string =
-  ## Converts `node` to its JSON Representation, with indentation and
+  ## Returns a JSON Representation of `node`, with indentation and
   ## on multiple lines.
   result = ""
   toPretty(result, node, indent)
@@ -1002,7 +1019,9 @@ proc toUgly*(result: var string, node: JsonNode) =
   ## regard for human readability. Meant to improve ``$`` string
   ## conversion performance.
   ##
-  ## This provides higher efficiency than the ``toPretty`` procedure as it
+  ## 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:
@@ -1325,10 +1344,26 @@ when isMainModule:
   var j4 = %*{"test": nil}
   doAssert j4 == %{"test": newJNull()}
 
-  echo("99% of tests finished. Going to try loading file.")
+  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:
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 84c8d3b11..2b903bedb 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -39,8 +39,6 @@ proc fac*(n: int): int {.noSideEffect.} =
 
 when defined(Posix) and not defined(haiku):
   {.passl: "-lm".}
-when not defined(js) and not defined(nimscript):
-  import times
 
 const
   PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
@@ -119,192 +117,191 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
   ## If `x` is empty, 0 is returned.
   for i in items(x): result = result + i
 
-proc random*(max: int): int {.benign.}
-  ## 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.
-
-proc random*(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. This has a 16-bit resolution on windows
-  ## and a 48-bit resolution on other platforms.
-
-when not defined(nimscript):
-  proc randomize*() {.benign.}
-    ## Initializes the random number generator with a "random"
-    ## number, i.e. a tickcount. Note: Does nothing for the JavaScript target,
-    ## as JavaScript does not support this. Nor does it work for NimScript.
-
-proc randomize*(seed: int) {.benign.}
-  ## Initializes the random number generator with a specific seed.
-  ## Note: Does nothing for the JavaScript target,
-  ## as JavaScript does not support this.
-
 {.push noSideEffect.}
 when not defined(JS):
-  proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".}
+  proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
+  proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".}
     ## Computes the square root of `x`.
-  proc cbrt*(x: float): float {.importc: "cbrt", header: "<math.h>".}
+  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: float): float {.importc: "log", header: "<math.h>".}
+  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`
-  proc log10*(x: float): float {.importc: "log10", header: "<math.h>".}
+  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: float): float = return ln(x) / ln(2.0)
+  proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0)
     ## Computes the binary logarithm (base 2) of `x`
-  proc exp*(x: float): float {.importc: "exp", header: "<math.h>".}
+  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 frexp*(x: float, exponent: var int): float {.
-    importc: "frexp", header: "<math.h>".}
-    ## 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.
-
-  proc round*(x: float): int {.importc: "lrint", header: "<math.h>".}
-    ## Converts a float to an int by rounding.
+  proc round0(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
+  proc round0(x: float64): float64 {.importc: "round", header: "<math.h>".}
+    ## Converts a float to an int by rounding.  Used internally by the round
+    ## function when the specified number of places is 0.
 
-  proc arccos*(x: float): float {.importc: "acos", header: "<math.h>".}
+  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: float): float {.importc: "asin", header: "<math.h>".}
+  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: float): float {.importc: "atan", header: "<math.h>".}
+  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: float): float {.importc: "atan2", header: "<math.h>".}
+  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 cos*(x: float): float {.importc: "cos", header: "<math.h>".}
+  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 cosh*(x: float): float {.importc: "cosh", header: "<math.h>".}
+
+  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 hypot*(x, y: float): float {.importc: "hypot", header: "<math.h>".}
+
+  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 sinh*(x: float): float {.importc: "sinh", header: "<math.h>".}
+  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 sin*(x: float): float {.importc: "sin", header: "<math.h>".}
+  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 tan*(x: float): float {.importc: "tan", header: "<math.h>".}
+
+  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 tanh*(x: float): float {.importc: "tanh", header: "<math.h>".}
+  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 pow*(x, y: float): float {.importc: "pow", header: "<math.h>".}
-    ## Computes `x` to power of `y`.
 
-  proc erf*(x: float): float {.importc: "erf", header: "<math.h>".}
+  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.
+
+  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: float): float {.importc: "erfc", header: "<math.h>".}
+  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 lgamma*(x: float): float {.importc: "lgamma", header: "<math.h>".}
+  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 tgamma*(x: float): float {.importc: "tgamma", header: "<math.h>".}
+  proc tgamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
+  proc tgamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
     ## The gamma function
 
-  # C procs:
-  when defined(vcc) and false:
-    # The "secure" random, available from Windows XP
-    # https://msdn.microsoft.com/en-us/library/sxtz2fa8.aspx
-    # Present in some variants of MinGW but not enough to justify
-    # `when defined(windows)` yet
-    proc rand_s(val: var cuint) {.importc: "rand_s", header: "<stdlib.h>".}
-    # To behave like the normal version
-    proc rand(): cuint = rand_s(result)
-  else:
-    proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>".}
-    proc rand(): cint {.importc: "rand", header: "<stdlib.h>".}
-
-  when not defined(windows):
-    proc srand48(seed: clong) {.importc: "srand48", header: "<stdlib.h>".}
-    proc drand48(): float {.importc: "drand48", header: "<stdlib.h>".}
-    proc random(max: float): float =
-      result = drand48() * max
-  else:
-    when defined(vcc): # Windows with Visual C
-      proc random(max: float): float =
-        # we are hardcoding this because
-        # importc-ing macros is extremely problematic
-        # and because the value is publicly documented
-        # on MSDN and very unlikely to change
-        # See https://msdn.microsoft.com/en-us/library/296az74e.aspx
-        const rand_max = 4294967295 # UINT_MAX
-        result = (float(rand()) / float(rand_max)) * max
-      proc randomize() = discard
-      proc randomize(seed: int) = discard
-    else: # Windows with another compiler
-      proc random(max: float): float =
-        # we are hardcoding this because
-        # importc-ing macros is extremely problematic
-        # and because the value is publicly documented
-        # on MSDN and very unlikely to change
-        const rand_max = 32767
-        result = (float(rand()) / float(rand_max)) * max
-
-  when not defined(vcc): # the above code for vcc uses `discard` instead
-    # this is either not Windows or is Windows without vcc
-    when not defined(nimscript):
-      proc randomize() =
-        randomize(cast[int](epochTime()))
-    proc randomize(seed: int) =
-      srand(cint(seed)) # rand_s doesn't use srand
-      when declared(srand48): srand48(seed)
-
-  proc random(max: int): int =
-    result = int(rand()) mod max
-
-  proc trunc*(x: float): float {.importc: "trunc", header: "<math.h>".}
+  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
     ##
     ## .. code-block:: nim
     ##  echo trunc(PI) # 3.0
-  proc floor*(x: float): float {.importc: "floor", header: "<math.h>".}
+
+  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`)
     ##
     ## .. code-block:: nim
     ##  echo floor(-3.5) ## -4.0
-  proc ceil*(x: float): float {.importc: "ceil", header: "<math.h>".}
+
+  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`)
     ##
     ## .. code-block:: nim
     ##  echo ceil(-2.1) ## -2.0
 
-  proc fmod*(x, y: float): float {.importc: "fmod", header: "<math.h>".}
+  proc fmod*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
+  proc fmod*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
     ## Computes the remainder of `x` divided by `y`
     ##
     ## .. code-block:: nim
     ##  echo fmod(-2.5, 0.3) ## -0.1
 
 else:
-  proc mathrandom(): float {.importc: "Math.random", nodecl.}
-  proc floor*(x: float): float {.importc: "Math.floor", nodecl.}
-  proc ceil*(x: float): float {.importc: "Math.ceil", nodecl.}
-  proc random(max: int): int =
-    result = int(floor(mathrandom() * float(max)))
-  proc random(max: float): float =
-    result = float(mathrandom() * float(max))
-  proc randomize() = discard
-  proc randomize(seed: int) = discard
-
-  proc sqrt*(x: float): float {.importc: "Math.sqrt", nodecl.}
-  proc ln*(x: float): float {.importc: "Math.log", nodecl.}
-  proc log10*(x: float): float = return ln(x) / ln(10.0)
-  proc log2*(x: float): float = return ln(x) / ln(2.0)
-
-  proc exp*(x: float): float {.importc: "Math.exp", nodecl.}
-  proc round*(x: float): int {.importc: "Math.round", nodecl.}
-  proc pow*(x, y: float): float {.importc: "Math.pow", nodecl.}
-
-  proc frexp*(x: float, exponent: var int): float =
+  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 sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
+  proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
+  proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
+  proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
+  proc log10*[T: float32|float64](x: T): T = return ln(x) / ln(10.0)
+  proc log2*[T: float32|float64](x: T): T = return ln(x) / ln(2.0)
+
+  proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
+  proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
+  proc round0(x: float): float {.importc: "Math.round", nodecl.}
+
+  proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
+  proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
+
+  proc arccos*(x: float32): float32 {.importc: "Math.acos", nodecl.}
+  proc arccos*(x: float64): float64 {.importc: "Math.acos", nodecl.}
+  proc arcsin*(x: float32): float32 {.importc: "Math.asin", nodecl.}
+  proc arcsin*(x: float64): float64 {.importc: "Math.asin", nodecl.}
+  proc arctan*(x: float32): float32 {.importc: "Math.atan", nodecl.}
+  proc arctan*(x: float64): float64 {.importc: "Math.atan", nodecl.}
+  proc arctan2*(y, x: float32): float32 {.importC: "Math.atan2", nodecl.}
+  proc arctan2*(y, x: float64): float64 {.importc: "Math.atan2", nodecl.}
+
+  proc cos*(x: float32): float32 {.importc: "Math.cos", nodecl.}
+  proc cos*(x: float64): float64 {.importc: "Math.cos", nodecl.}
+  proc cosh*(x: float32): float32 = return (exp(x)+exp(-x))*0.5
+  proc cosh*(x: float64): float64 = return (exp(x)+exp(-x))*0.5
+  proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
+  proc sinh*[T: float32|float64](x: T): T = return (exp(x)-exp(-x))*0.5
+  proc sin*(x: float32): float32 {.importc: "Math.sin", nodecl.}
+  proc sin*(x: float64): float64 {.importc: "Math.sin", nodecl.}
+  proc tan*(x: float32): float32 {.importc: "Math.tan", nodecl.}
+  proc tan*(x: float64): float64 {.importc: "Math.tan", nodecl.}
+  proc tanh*[T: float32|float64](x: T): T =
+    var y = exp(2.0*x)
+    return (y-1.0)/(y+1.0)
+
+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`
+  if places == 0:
+    result = round0(x)
+  else:
+    var mult = pow(10.0, places.T)
+    result = round0(x*mult)/mult
+
+when not defined(JS):
+  proc frexp*(x: float32, exponent: var int): float32 {.
+    importc: "frexp", header: "<math.h>".}
+  proc frexp*(x: float64, exponent: var int): float64 {.
+    importc: "frexp", header: "<math.h>".}
+    ## 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.
+else:
+  proc frexp*[T: float32|float64](x: T, exponent: var int): T =
     if x == 0.0:
       exponent = 0
       result = 0.0
@@ -315,20 +312,22 @@ else:
       exponent = round(ex)
       result = x / pow(2.0, ex)
 
-  proc arccos*(x: float): float {.importc: "Math.acos", nodecl.}
-  proc arcsin*(x: float): float {.importc: "Math.asin", nodecl.}
-  proc arctan*(x: float): float {.importc: "Math.atan", nodecl.}
-  proc arctan2*(y, x: float): float {.importc: "Math.atan2", nodecl.}
-
-  proc cos*(x: float): float {.importc: "Math.cos", nodecl.}
-  proc cosh*(x: float): float = return (exp(x)+exp(-x))*0.5
-  proc hypot*(x, y: float): float = return sqrt(x*x + y*y)
-  proc sinh*(x: float): float = return (exp(x)-exp(-x))*0.5
-  proc sin*(x: float): float {.importc: "Math.sin", nodecl.}
-  proc tan*(x: float): float {.importc: "Math.tan", nodecl.}
-  proc tanh*(x: float): float =
-    var y = exp(2.0*x)
-    return (y-1.0)/(y+1.0)
+proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
+  ## Breaks `x` into an integral and a fractional part.
+  ##
+  ## 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.
+  var
+    absolute: T
+  absolute = abs(x)
+  result.intpart = floor(absolute)
+  result.floatpart = absolute - result.intpart
+  if x < 0:
+    result.intpart = -result.intpart
+    result.floatpart = -result.floatpart
 
 {.pop.}
 
@@ -340,7 +339,7 @@ proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
   ## Convert from radians to degrees
   result = T(d) / RadPerDeg
 
-proc `mod`*(x, y: float): float =
+proc `mod`*[T: float32|float64](x, y: T): T =
   ## Computes the modulo operation for float operators. Equivalent
   ## to ``x - y * floor(x/y)``. Note that the remainder will always
   ## have the same sign as the divisor.
@@ -349,14 +348,6 @@ proc `mod`*(x, y: float): float =
   ##  echo (4.0 mod -3.1) # -2.2
   result = if y == 0.0: x else: x - y * (x/y).floor
 
-proc random*[T](x: Slice[T]): T =
-  ## For a slice `a .. b` returns a value in the range `a .. b-1`.
-  result = random(x.b - x.a) + x.a
-
-proc random*[T](a: openArray[T]): T =
-  ## returns a random element from the openarray `a`.
-  result = a[random(a.low..a.len)]
-
 {.pop.}
 {.pop.}
 
@@ -391,24 +382,6 @@ proc lcm*[T](x, y: T): T =
   x div gcd(x, y) * y
 
 when isMainModule and not defined(JS):
-  proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
-
-  # Verifies random seed initialization.
-  let seed = gettime(nil)
-  randomize(seed)
-  const SIZE = 10
-  var buf : array[0..SIZE, int]
-  # Fill the buffer with random values
-  for i in 0..SIZE-1:
-    buf[i] = random(high(int))
-  # Check that the second random calls are the same for each position.
-  randomize(seed)
-  for i in 0..SIZE-1:
-    assert buf[i] == random(high(int)), "non deterministic random seeding"
-
-  when not defined(testing):
-    echo "random values equal after reseeding"
-
   # Check for no side effect annotation
   proc mySqrt(num: float): float {.noSideEffect.} =
     return sqrt(num)
@@ -418,3 +391,36 @@ when isMainModule and not defined(JS):
   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: # 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
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 043d6d80a..15828ff35 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -27,7 +27,7 @@ else:
   import posix
   export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
     EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
-  export Sockaddr_storage
+  export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
 
 export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
   Sockaddr_in6,
@@ -38,7 +38,7 @@ export
   SOL_SOCKET,
   SOMAXCONN,
   SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE,
-  SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR,
+  SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, SO_REUSEPORT,
   MSG_PEEK
 
 when defined(macosx) and not defined(nimdoc):
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 5de6667dd..cb8cea720 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -68,6 +68,7 @@
 {.deadCodeElim: on.}
 import nativesockets, os, strutils, parseutils, times
 export Port, `$`, `==`
+export Domain, SockType, Protocol
 
 const useWinVersion = defined(Windows) or defined(nimdoc)
 const defineSsl = defined(ssl) or defined(nimdoc)
@@ -129,7 +130,7 @@ type
 
   SOBool* = enum ## Boolean socket options.
     OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
-    OptOOBInline, OptReuseAddr
+    OptOOBInline, OptReuseAddr, OptReusePort
 
   ReadLineResult* = enum ## result for readLineAsync
     ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
@@ -579,6 +580,7 @@ proc toCInt*(opt: SOBool): cint =
   of OptKeepAlive: SO_KEEPALIVE
   of OptOOBInline: SO_OOBINLINE
   of OptReuseAddr: SO_REUSEADDR
+  of OptReusePort: SO_REUSEPORT
 
 proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
   tags: [ReadIOEffect].} =
@@ -604,9 +606,35 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
   var valuei = cint(if value: 1 else: 0)
   setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
 
-when defineSsl:
+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):
+  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:
+        raiseOSError(osLastError())
+
+  proc bindUnix*(socket: Socket, path: string) =
+    ## 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),
+                            sizeof(socketAddr).Socklen) != 0'i32:
+        raiseOSError(osLastError())
+
+when defined(ssl):
   proc handshake*(socket: Socket): bool
-      {.tags: [ReadIOEffect, WriteIOEffect], deprecated.} =
+    {.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.
@@ -1374,7 +1402,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
   if selectWrite(s, timeout) != 1:
     raise newException(TimeoutError, "Call to 'connect' timed out.")
   else:
-    when defineSsl:
+    when defineSsl and not defined(nimdoc):
       if socket.isSSL:
         socket.fd.setBlocking(true)
         {.warning[Deprecated]: off.}
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 38b0ed4a3..6d528652a 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -875,8 +875,9 @@ elif not defined(useNimRtl):
       var error: cint
       let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
       if sizeRead == sizeof(error):
-        raiseOSError("Could not find command: '$1'. OS error: $2" %
-          [$data.sysCommand, $strerror(error)])
+        raiseOSError(osLastError(),
+                     "Could not find command: '$1'. OS error: $2" %
+                     [$data.sysCommand, $strerror(error)])
 
       return pid
 
@@ -966,17 +967,169 @@ elif not defined(useNimRtl):
   proc kill(p: Process) =
     if kill(p.id, SIGKILL) != 0'i32:
       raiseOsError(osLastError())
+  
+  when defined(macosx) or defined(freebsd) or defined(netbsd) or
+       defined(openbsd):
+    import kqueue, times
+
+    proc waitForExit(p: Process, timeout: int = -1): int =
+      if p.exitCode != -3: return p.exitCode
+      if timeout == -1:
+        if waitpid(p.id, p.exitCode, 0) < 0:
+          p.exitCode = -3
+          raiseOSError(osLastError())
+      else:
+        var kqFD = kqueue()
+        if kqFD == -1:
+          raiseOSError(osLastError())
+
+        var kevIn = KEvent(ident: p.id.uint, filter: EVFILT_PROC,
+                         flags: EV_ADD, fflags: NOTE_EXIT)
+        var kevOut: KEvent
+        var tmspec: Timespec
+
+        if timeout >= 1000:
+          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
+        else:
+          tmspec.tv_sec = 0.Time
+          tmspec.tv_nsec = (timeout * 1_000_000)
+
+        try:
+          while true:
+            var count = kevent(kqFD, addr(kevIn), 1, addr(kevOut), 1,
+                               addr(tmspec))
+            if count < 0:
+              let err = osLastError()
+              if err.cint != EINTR:
+                raiseOSError(osLastError())
+            elif count == 0:
+              # timeout expired, so we trying to kill process
+              if posix.kill(p.id, SIGKILL) == -1:
+                raiseOSError(osLastError())
+              if waitpid(p.id, p.exitCode, 0) < 0:
+                p.exitCode = -3
+                raiseOSError(osLastError())
+              break
+            else:
+              if kevOut.ident == p.id.uint and kevOut.filter == EVFILT_PROC:
+                if waitpid(p.id, p.exitCode, 0) < 0:
+                  p.exitCode = -3
+                  raiseOSError(osLastError())
+                break
+              else:
+                raiseOSError(osLastError())
+        finally:
+          discard posix.close(kqFD)
+
+      result = int(p.exitCode) shr 8
+  else:
+    import times
+
+    const
+      hasThreadSupport = compileOption("threads") and not defined(nimscript)
+
+    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).Time
+        if e.tv_nsec >= s.tv_nsec:
+          e.tv_nsec -= s.tv_nsec
+        else:
+          if e.tv_sec == 0.Time:
+            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).Time
+        if t.tv_nsec >= e.tv_nsec:
+          t.tv_nsec -= e.tv_nsec
+        else:
+          t.tv_sec = (int(t.tv_sec) - 1).Time
+          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
+
+      #if waitPid(p.id, p.exitCode, 0) == int(p.id):
+      # ``waitPid`` fails if the process is not running anymore. But then
+      # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
+      # initialized with -3, wrong success exit codes are prevented.
+      if p.exitCode != -3: return p.exitCode
+      if timeout == -1:
+        if waitpid(p.id, p.exitCode, 0) < 0:
+          p.exitCode = -3
+          raiseOSError(osLastError())
+      else:
+        var nmask, omask: Sigset
+        var sinfo: SigInfo
+        var stspec, enspec, tmspec: Timespec
 
-  proc waitForExit(p: Process, timeout: int = -1): int =
-    #if waitPid(p.id, p.exitCode, 0) == int(p.id):
-    # ``waitPid`` fails if the process is not running anymore. But then
-    # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
-    # initialized with -3, wrong success exit codes are prevented.
-    if p.exitCode != -3: return p.exitCode
-    if waitpid(p.id, p.exitCode, 0) < 0:
-      p.exitCode = -3
-      raiseOSError(osLastError())
-    result = int(p.exitCode) shr 8
+        discard sigemptyset(nmask)
+        discard sigemptyset(omask)
+        discard sigaddset(nmask, SIGCHLD)
+
+        when hasThreadSupport:
+          if pthread_sigmask(SIG_BLOCK, nmask, omask) == -1:
+            raiseOSError(osLastError())
+        else:
+          if sigprocmask(SIG_BLOCK, nmask, omask) == -1:
+            raiseOSError(osLastError())
+
+        if timeout >= 1000:
+          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
+        else:
+          tmspec.tv_sec = 0.Time
+          tmspec.tv_nsec = (timeout * 1_000_000)
+
+        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:
+                if waitpid(p.id, p.exitCode, 0) < 0:
+                  p.exitCode = -3
+                  raiseOSError(osLastError())
+                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())
+                if waitpid(p.id, p.exitCode, 0) < 0:
+                  p.exitCode = -3
+                  raiseOSError(osLastError())
+                break
+              else:
+                raiseOSError(err)
+        finally:
+          when hasThreadSupport:
+            if pthread_sigmask(SIG_UNBLOCK, nmask, omask) == -1:
+              raiseOSError(osLastError())
+          else:
+            if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
+              raiseOSError(osLastError())
+
+      result = int(p.exitCode) shr 8
 
   proc peekExitCode(p: Process): int =
     if p.exitCode != -3: return p.exitCode
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 9bcac0a50..25879d2b7 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -15,17 +15,78 @@
 
 ## This is an example of how a configuration file may look like:
 ##
-## .. include:: doc/mytest.cfg
+## .. 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
-
+##     :file: ../../examples/parsecfgex.nim
+##
+## Examples
+## --------
+##
+## This is an example of a configuration file.
+##
+## ::
+##
+##     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
+  hashes, strutils, lexbase, streams, tables
 
 include "system/inclrtl"
 
@@ -359,3 +420,138 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
     result.kind = cfgError
     result.msg = errorStr(c, "invalid token: " & c.tok.literal)
     rawGetTok(c, c.tok)
+
+# ---------------- Configuration file related operations ----------------
+type
+  Config* = OrderedTableRef[string, OrderedTableRef[string, string]]
+
+proc newConfig*(): Config =
+  ## Create a new configuration table.
+  ## Useful when wanting to create a configuration file.
+  result = newOrderedTable[string, OrderedTableRef[string, string]]()
+
+proc loadConfig*(filename: string): Config =
+  ## Load the specified configuration file into a new Config instance.
+  var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
+  var curSection = "" ## Current section,
+                      ## the default value of the current section is "",
+                      ## which means that the current section is a common
+  var p: CfgParser
+  var fileStream = newFileStream(filename, fmRead)
+  if fileStream != nil:
+    open(p, fileStream, filename)
+    while true:
+      var e = next(p)
+      case e.kind
+      of cfgEof:
+        break
+      of cfgSectionStart: # Only look for the first time the Section
+        curSection = e.section
+      of cfgKeyValuePair:
+        var t = newOrderedTable[string, string]()
+        if dict.hasKey(curSection):
+          t = dict[curSection]
+        t[e.key] = e.value
+        dict[curSection] = t
+      of cfgOption:
+        var c = newOrderedTable[string, string]()
+        if dict.hasKey(curSection):
+          c = dict[curSection]
+        c["--" & e.key] = e.value
+        dict[curSection] = c
+      of cfgError:
+        break
+    close(p)
+  result = dict
+
+proc replace(s: string): string =
+  var d = ""
+  var i = 0
+  while i < s.len():
+    if s[i] == '\\':
+      d.add(r"\\")
+    elif s[i] == '\c' and s[i+1] == '\L':
+      d.add(r"\n")
+      inc(i)
+    elif s[i] == '\c':
+      d.add(r"\n")
+    elif s[i] == '\L':
+      d.add(r"\n")
+    else:
+      d.add(s[i])
+    inc(i)
+  result = d
+
+proc writeConfig*(dict: Config, filename: string) =
+  ## Writes the contents of the table to the specified configuration file.
+  ## Note: Comment statement will be ignored.
+  var file: File
+  if file.open(filename, fmWrite):
+    try:
+      var section, key, value, kv, segmentChar:string
+      for pair in dict.pairs():
+        section = pair[0]
+        if section != "": ## Not general section
+          if not allCharsInSet(section, SymChars): ## Non system character
+            file.writeLine("[\"" & section & "\"]")
+          else:
+            file.writeLine("[" & section & "]")
+        for pair2 in pair[1].pairs():
+          key = pair2[0]
+          value = pair2[1]
+          if key[0] == '-' and key[1] == '-': ## If it is a command key
+            segmentChar = ":"
+            if not allCharsInSet(key[2..key.len()-1], SymChars):
+              kv.add("--\"")
+              kv.add(key[2..key.len()-1])
+              kv.add("\"")
+            else:
+              kv = key
+          else:
+            segmentChar = "="
+            kv = key
+          if value != "": ## If the key is not empty
+            if not allCharsInSet(value, SymChars):
+              kv.add(segmentChar)
+              kv.add("\"")
+              kv.add(replace(value))
+              kv.add("\"")
+            else:
+              kv.add(segmentChar)
+              kv.add(value)
+          file.writeLine(kv)
+    except:
+      raise
+    finally:
+      file.close()
+
+proc getSectionValue*(dict: Config, section, key: string): string =
+  ## Gets the Key value of the specified Section.
+  if dict.haskey(section):
+    if dict[section].hasKey(key):
+      result = dict[section][key]
+    else:
+      result = ""
+  else:
+    result = ""
+
+proc setSectionKey*(dict: var Config, section, key, value: string) =
+  ## Sets the Key value of the specified Section.
+  var t = newOrderedTable[string, string]()
+  if dict.hasKey(section):
+    t = dict[section]
+  t[key] = value
+  dict[section] = t
+
+proc delSection*(dict: var Config, section: string) =
+  ## Deletes the specified section and all of its sub keys.
+  dict.del(section)
+
+proc delSectionKey*(dict: var Config, section, key: string) =
+  ## Delete the key of the specified section.
+  if dict.haskey(section):
+    if dict[section].hasKey(key):
+      if dict[section].len() == 1:
+        dict.del(section)
+      else:
+        dict[section].del(key)
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index af51e1201..bb291bcbc 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -77,6 +77,15 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
   my.row = @[]
   my.currRow = 0
 
+proc open*(my: var CsvParser, filename: string,
+           separator = ',', quote = '"', escape = '\0',
+           skipInitialSpace = false) =
+  ## same as the other `open` but creates the file stream for you.
+  var s = newFileStream(filename, fmRead)
+  if s == nil: my.error(0, "cannot open: " & filename)
+  open(my, s, filename, separator,
+       quote, escape, skipInitialSpace)
+
 proc parseField(my: var CsvParser, a: var string) =
   var pos = my.bufpos
   var buf = my.buf
@@ -131,6 +140,8 @@ proc readRow*(my: 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.
   var col = 0 # current column
   var oldpos = my.bufpos
   while my.buf[my.bufpos] != '\0':
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index f8b2c3d8d..06daa3782 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -34,7 +34,7 @@
 ## document.
 ##
 ## .. code-block:: nim
-##     :file: examples/htmltitle.nim
+##     :file: ../../examples/htmltitle.nim
 ##
 ##
 ## Example 2: Retrieve all HTML links
@@ -45,7 +45,7 @@
 ## an HTML document contains.
 ##
 ## .. code-block:: nim
-##     :file: examples/htmlrefs.nim
+##     :file: ../../examples/htmlrefs.nim
 ##
 
 import
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index eea20a62c..7e1f50266 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -12,7 +12,7 @@
 ## Matching performance is hopefully competitive with optimized regular
 ## expression engines.
 ##
-## .. include:: ../doc/pegdocs.txt
+## .. include:: ../../doc/pegdocs.txt
 ##
 
 include "system/inclrtl"
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
new file mode 100644
index 000000000..c73f403eb
--- /dev/null
+++ b/lib/pure/random.nim
@@ -0,0 +1,129 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+##[Nim's standard random number generator. Based on
+
+| `http://xoroshiro.di.unimi.it/`_
+| `http://xoroshiro.di.unimi.it/xoroshiro128plus.c`_
+]##
+
+include "system/inclrtl"
+{.push debugger:off.}
+
+# XXX Expose RandomGenState
+when defined(JS):
+  type ui = uint32
+else:
+  type ui = uint64
+
+type
+  RandomGenState = object
+    a0, a1: ui
+
+when defined(JS):
+  var state = RandomGenState(
+    a0: 0x69B4C98Cu32,
+    a1: 0xFED1DD30u32) # global for backwards compatibility
+else:
+  # racy for multi-threading but good enough for now:
+  var state = RandomGenState(
+    a0: 0x69B4C98CB8530805u64,
+    a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
+
+proc rotl(x, k: ui): ui =
+  result = (x shl k) or (x shr (ui(64) - k))
+
+proc next(s: var RandomGenState): uint64 =
+  let s0 = s.a0
+  var s1 = s.a1
+  result = s0 + s1
+  s1 = s1 xor s0
+  s.a0 = rotl(s0, 55) xor s1 xor (s1 shl 14) # a, b
+  s.a1 = rotl(s1, 36) # c
+
+proc skipRandomNumbers(s: var RandomGenState) =
+  ## 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:
+    const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
+  var
+    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:
+        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.} =
+  ## 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.
+  result = int(next(state) mod uint64(max))
+
+proc random*(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.
+  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: Slice[T]): T =
+  ## For a slice `a .. b` returns a value in the range `a .. b-1`.
+  result = random(x.b - x.a) + x.a
+
+proc random*[T](a: openArray[T]): T =
+  ## returns a random element from the openarray `a`.
+  result = a[random(a.low..a.len)]
+
+proc randomize*(seed: int) {.benign.} =
+  ## Initializes the random number generator with a specific seed.
+  state.a0 = ui(seed shr 16)
+  state.a1 = ui(seed and 0xffff)
+
+when not defined(nimscript):
+  import times
+
+  proc randomize*() {.benign.} =
+    ## Initializes the random number generator with a "random"
+    ## number, i.e. a tickcount. Note: Does not work for NimScript.
+    when defined(JS):
+      proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
+      randomize(getMil times.getTime())
+    else:
+      randomize(int times.getTime())
+
+{.pop.}
+
+when isMainModule:
+  proc main =
+    var occur: array[1000, int]
+
+    var x = 8234
+    for i in 0..100_000:
+      x = random(len(occur)) # myrand(x)
+      inc occur[x]
+    for i, oc in occur:
+      if oc < 69:
+        doAssert false, "too few occurances of " & $i
+      elif oc > 130:
+        doAssert false, "too many occurances of " & $i
+  main()
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index 6fd05dc4b..bf134f2ae 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -41,26 +41,26 @@ proc toRational*[T:SomeInteger](x: T): Rational[T] =
 
 proc toRationalSub(x: float, n: int): Rational[int] =
   var
-    a = 0
-    b, c, d = 1
+    a = 0'i64
+    b, c, d = 1'i64
   result = 0 // 1   # rational 0
   while b <= n and d <= n:
     let ac = (a+c)
     let bd = (b+d)
     # scale by 1000 so not overflow for high precision
-    let mediant = (ac/1000) / (bd/1000)
+    let mediant = (ac.float/1000) / (bd.float/1000)
     if x == mediant:
       if bd <= n:
-        result.num = ac
-        result.den = bd
+        result.num = ac.int
+        result.den = bd.int
         return result
       elif d > b:
-        result.num = c
-        result.den = d
+        result.num = c.int
+        result.den = d.int
         return result
       else:
-        result.num = a
-        result.den = b
+        result.num = a.int
+        result.den = b.int
         return result
     elif x > mediant:
       a = ac
@@ -69,8 +69,8 @@ proc toRationalSub(x: float, n: int): Rational[int] =
       c = ac
       d = bd
   if (b > n):
-    return initRational(c, d)
-  return initRational(a, b)
+    return initRational(c.int, d.int)
+  return initRational(a.int, b.int)
 
 proc toRational*(x: float, n: int = high(int)): Rational[int] =
   ## Calculate the best rational numerator and denominator
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index c3d6d75bd..be80685ab 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -14,6 +14,7 @@
 ## <backends.html#the-javascript-target>`_.
 
 import parseutils
+from math import pow, round, floor, log10
 
 {.deadCodeElim: on.}
 
@@ -324,7 +325,7 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
     result[i] = chr(val mod 8 + ord('0'))
     val = val div 8
 
-iterator split*(s: string, seps: set[char] = Whitespace): string =
+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`. Note
@@ -369,15 +370,19 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
   ##   "08.398990"
   ##
   var last = 0
+  var splits = maxsplit
   assert(not ('\0' in seps))
   while last < len(s):
     while s[last] in seps: inc(last)
     var first = last
     while last < len(s) and s[last] notin seps: inc(last) # BUGFIX!
     if first <= last-1:
+      if splits == 0: last = len(s)
       yield substr(s, first, last-1)
+      if splits == 0: break
+      dec(splits)
 
-iterator split*(s: string, sep: char): string =
+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`.
@@ -404,26 +409,39 @@ iterator split*(s: string, sep: char): string =
   ##   ""
   ##
   var last = 0
+  var splits = maxsplit
   assert('\0' != sep)
   if len(s) > 0:
     # `<=` is correct here for the edge cases!
     while last <= len(s):
       var first = last
       while last < len(s) and 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)
 
-iterator split*(s: string, sep: string): string =
+proc substrEq(s: string, a, L: int, x: string): bool =
+  var i = 0
+  while i < L and s[a+i] == x[i]: inc i
+  result = i == L
+
+iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a string separator.
   ##
   ## Substrings are separated by the string `sep`.
   var last = 0
+  var splits = maxsplit
   if len(s) > 0:
     while last <= len(s):
       var first = last
-      while last < len(s) and s.substr(last, last + <sep.len) != sep:
+      while last < len(s) and not s.substrEq(last, sep.len, sep):
         inc(last)
+      if splits == 0: last = len(s)
       yield substr(s, first, last-1)
+      if splits == 0: break
+      dec(splits)
       inc(last, sep.len)
 
 iterator splitLines*(s: string): string =
@@ -493,25 +511,25 @@ proc countLines*(s: string): int {.noSideEffect,
     else: discard
     inc i
 
-proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
+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]>`_, but is a
   ## proc that returns a sequence of substrings.
-  accumulateResult(split(s, seps))
+  accumulateResult(split(s, seps, maxsplit))
 
-proc split*(s: string, sep: char): seq[string] {.noSideEffect,
+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>`_, but is a proc
   ## that returns a sequence of substrings.
-  accumulateResult(split(s, sep))
+  accumulateResult(split(s, sep, maxsplit))
 
-proc split*(s: string, sep: string): seq[string] {.noSideEffect,
+proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitString".} =
   ## Splits the string `s` into substrings using a string separator.
   ##
   ## Substrings are separated by the string `sep`. This is a wrapper around the
   ## `split iterator <#split.i,string,string>`_.
-  accumulateResult(split(s, sep))
+  accumulateResult(split(s, sep, maxsplit))
 
 proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
   rtl, extern: "nsuToHex".} =
@@ -530,6 +548,10 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
     # handle negative overflow
     if n == 0 and x < 0: n = -1
 
+proc toHex*[T](x: T): string =
+  ## Shortcut for ``toHex(x, T.sizeOf * 2)``
+  toHex(x, T.sizeOf * 2)
+
 proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
   rtl, extern: "nsuIntToStr".} =
   ## Converts `x` to its decimal representation.
@@ -1444,28 +1466,216 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
   ## after the decimal point for Nim's ``float`` type.
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
-proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
-  ## Rounds and formats `bytes`. Examples:
+proc trimZeros*(x: var string) {.noSideEffect.} =
+  ## 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]
+
+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
+
+proc formatSize*(bytes: int64,
+                 decimalSep = '.',
+                 prefix = bpIEC,
+                 includeSpace = false): string {.noSideEffect.} =
+  ## Rounds and formats `bytes`.
+  ##
+  ## By default, uses the IEC/ISO standard binary prefixes, so 1024 will be
+  ## formatted as 1KiB.  Set prefix to `bpColloquial` to use the colloquial
+  ## names from the SI standard (e.g. k for 1000 being reused as 1024).
+  ##
+  ## `includeSpace` can be set to true to include the (SI preferred) space
+  ## between the number and the unit (e.g. 1 KiB).
+  ##
+  ## Examples:
   ##
   ## .. code-block:: nim
   ##
-  ##    formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
-  ##    formatSize(4096) == "4KB"
-  ##
-  template frmt(a, b, c: expr): expr =
-    let bs = $b
-    insertSep($a) & decimalSep & bs.substr(0, 2) & c
-  let gigabytes = bytes shr 30
-  let megabytes = bytes shr 20
-  let kilobytes = bytes shr 10
-  if gigabytes != 0:
-    result = frmt(gigabytes, megabytes, "GB")
-  elif megabytes != 0:
-    result = frmt(megabytes, kilobytes, "MB")
-  elif kilobytes != 0:
-    result = frmt(kilobytes, bytes, "KB")
+  ##    formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
+  ##    formatSize((2.234*1024*1024).int) == "2.234MiB"
+  ##    formatSize(4096, includeSpace=true) == "4 KiB"
+  ##    formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+  ##    formatSize(4096) == "4KiB"
+  ##    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
+    prefixes: array[9, string]
+  if prefix == bpColloquial:
+    prefixes = collPrefixes
   else:
-    result = insertSep($bytes) & "B"
+    prefixes = iecPrefixes
+
+  # Iterate through prefixes seeing if value will be greater than
+  # 0 in each case
+  for index in 1..<prefixes.len:
+    last_xb = xb
+    xb = bytes div (1'i64 shl (index*10))
+    matchedIndex = index
+    if xb == 0:
+      xb = last_xb
+      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()
+  if includeSpace:
+    result &= " "
+  result &= prefixes[matchedIndex]
+  result &= "B"
+
+proc formatEng*(f: BiggestFloat,
+                precision: range[0..32] = 10,
+                trim: bool = true,
+                siPrefix: bool = false,
+                unit: string = nil,
+                decimalSep = '.'): string {.noSideEffect.} =
+  ## 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
+  ## exponent.  Numbers outside of this range will be formatted as a
+  ## significand in the range -1000.0<f<1000.0 and an exponent that will always
+  ## be an integer multiple of 3, corresponding with the SI prefix scale k, M,
+  ## G, T etc for numbers with an absolute value greater than 1 and m, μ, n, p
+  ## etc for numbers with an absolute value less than 1.
+  ##
+  ## The default configuration (`trim=true` and `precision=10`) shows the
+  ## **shortest** form that precisely (up to a maximum of 10 decimal places)
+  ## displays the value.  For example, 4.100000 will be displayed as 4.1 (which
+  ## is mathematically identical) whereas 4.1000003 will be displayed as
+  ## 4.1000003.
+  ##
+  ## If `trim` is set to true, trailing zeros will be removed; if false, the
+  ## number of digits specified by `precision` will always be shown.
+  ##
+  ## `precision` can be used to set the number of digits to be shown after the
+  ## decimal point or (if `trim` is true) the maximum number of digits to be
+  ## shown.
+  ##
+  ## .. code-block:: 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
+  ## as "4.1 k" instead of "4.1e3".  Note that `u` is used for micro- in place
+  ## of the greek letter mu (μ) as per ISO 2955.  Numbers with an absolute
+  ## value outside of the range 1e-18<f<1000e18 (1a<f<1000E) will be displayed
+  ## with an exponent rather than an SI prefix, regardless of whether
+  ## `siPrefix` is true.
+  ##
+  ## If `unit` is not nil, the provided unit will be appended to the string
+  ## (with a space as required by the SI standard).  This behaviour is slightly
+  ## 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
+  ##
+  ##    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
+  ##    formatEng(4100, siPrefix=true) == "4.1 k"
+  ##    formatEng(4.1, siPrefix=true, unit="") == "4.1 " # Space with unit=""
+  ##    formatEng(4100, siPrefix=true, unit="") == "4.1 k"
+  ##    formatEng(4100) == "4.1e3"
+  ##    formatEng(4100, unit="V") == "4.1e3 V"
+  ##    formatEng(4100, unit="") == "4.1e3 " # Space with unit=""
+  ##
+  ## `decimalSep` is used as the decimal separator
+  var
+    absolute: BiggestFloat
+    significand: BiggestFloat
+    fexponent: BiggestFloat
+    exponent: int
+    splitResult: seq[string]
+    suffix: string = ""
+  proc 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']
+    var index: int = (exp div 3) + 6
+    result = ' '
+    if index in low(siPrefixes)..high(siPrefixes):
+      result = siPrefixes[index]
+
+  # Most of the work is done with the sign ignored, so get the absolute value
+  absolute = abs(f)
+  significand = f
+
+  if absolute == 0.0:
+    # Simple case: just format it and force the exponent to 0
+    exponent = 0
+    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))
+    # Adjust the significand for the new exponent
+    significand /= pow(10.0, fexponent)
+
+    # Round 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='.')
+    exponent = fexponent.int()
+
+  splitResult = result.split('.')
+  result = splitResult[0]
+  # result should have at most one decimal character
+  if splitResult.len() > 1:
+    # If trim is set, we get rid of trailing zeros.  Don't use trimZeros here as
+    # 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]
+    else:
+      result &= decimalSep & splitResult[1]
+
+  # Combine the results accordingly
+  if siPrefix and exponent != 0:
+    var p = getPrefix(exponent)
+    if p != ' ':
+      suffix = " " & p
+      exponent = 0 # Exponent replaced by SI prefix
+  if suffix == "" and unit != nil:
+    suffix = " "
+  if unit != nil:
+    suffix &= unit
+  if exponent != 0:
+    result &= "e" & $exponent
+  result &= suffix
 
 proc findNormalized(x: string, inArray: openArray[string]): int =
   var i = 0
@@ -1661,9 +1871,14 @@ when isMainModule:
                                                    ["1,0e-11", "1,0e-011"]
 
   doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
-  when not defined(testing):
-    echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
-    echo formatSize(1'i64 shl 31)
+
+  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"
 
   doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
            "The cat eats fish."
@@ -1743,6 +1958,7 @@ when isMainModule:
   doAssert isUpper("ABC")
   doAssert(not isUpper("AAcc"))
   doAssert(not isUpper("A#$"))
+
   doAssert(unescape(r"\x013", "", "") == "\x013")
 
   doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
@@ -1778,4 +1994,39 @@ bar
     bar
   """.unindent() == "foo\nfoo\nbar\n"
 
-  echo("strutils tests passed")
+  let s = " this   is     an example   "
+  doAssert s.split() == @["this", "is", "an", "example"]
+  doAssert s.split(maxsplit=4) == @["this", "is", "an", "example"]
+  doAssert s.split(' ', maxsplit=4) == @["", "this", "", "", "is     an example   "]
+  doAssert s.split(" ", maxsplit=4) == @["", "this", "", "", "is     an example   "]
+
+  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") == "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="") == "4.1 " # Includes space
+    doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
+    doAssert formatEng(4100) == "4.1e3"
+    doAssert formatEng(4100, unit="V") == "4.1e3 V"
+    doAssert formatEng(4100, unit="") == "4.1e3 " # Space with unit=""
+    # Don't use SI prefix as number is too big
+    doAssert formatEng(3.1e22, siPrefix=true, unit="a") == "31e21 a"
+    # Don't use SI prefix as number is too small
+    doAssert formatEng(3.1e-25, siPrefix=true, unit="A") == "310e-27 A"
+
+  #echo("strutils tests passed")
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
index 22f29b77c..351b3c086 100644
--- a/lib/pure/subexes.nim
+++ b/lib/pure/subexes.nim
@@ -9,7 +9,7 @@
 
 ## Nim support for `substitution expressions`:idx: (`subex`:idx:).
 ##
-## .. include:: ../doc/subexes.txt
+## .. include:: ../../doc/subexes.txt
 ##
 
 {.push debugger:off .} # the user does not want to trace a part
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index e0ee884a8..ac8dc93ad 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -64,8 +64,9 @@ when defined(posix) and not defined(JS):
   proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
     importc: "gettimeofday", header: "<sys/time.h>".}
 
-  var
-    timezone {.importc, header: "<time.h>".}: int
+  when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
+    var timezone {.importc, header: "<time.h>".}: int
+  var  
     tzname {.importc, header: "<time.h>" .}: array[0..1, cstring]
   # we also need tzset() to make sure that tzname is initialized
   proc tzset() {.importc, header: "<time.h>".}
@@ -94,7 +95,8 @@ elif defined(windows):
 
 elif defined(JS):
   type
-    Time* {.importc.} = object
+    Time* = ref TimeObj
+    TimeObj {.importc.} = object
       getDay: proc (): int {.tags: [], raises: [], benign.}
       getFullYear: proc (): int {.tags: [], raises: [], benign.}
       getHours: proc (): int {.tags: [], raises: [], benign.}
@@ -416,18 +418,32 @@ when not defined(JS):
 
 when not defined(JS):
   # C wrapper:
+  when defined(freebsd) or defined(netbsd) or defined(openbsd):
+    type
+      StructTM {.importc: "struct tm", final.} = 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", final.} = 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
   type
-    StructTM {.importc: "struct tm", final.} = 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
-
     TimeInfoPtr = ptr StructTM
     Clock {.importc: "clock_t".} = distinct int
 
@@ -457,24 +473,47 @@ when not defined(JS):
     const
       weekDays: array [0..6, WeekDay] = [
         dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-    TimeInfo(second: int(tm.second),
-      minute: int(tm.minute),
-      hour: int(tm.hour),
-      monthday: int(tm.monthday),
-      month: Month(tm.month),
-      year: tm.year + 1900'i32,
-      weekday: weekDays[int(tm.weekday)],
-      yearday: int(tm.yearday),
-      isDST: tm.isdst > 0,
-      tzname: if local:
-          if tm.isdst > 0:
-            getTzname().DST
+    when defined(freebsd) or defined(netbsd) or defined(openbsd):
+      TimeInfo(second: int(tm.second),
+        minute: int(tm.minute),
+        hour: int(tm.hour),
+        monthday: int(tm.monthday),
+        month: Month(tm.month),
+        year: tm.year + 1900'i32,
+        weekday: weekDays[int(tm.weekday)],
+        yearday: int(tm.yearday),
+        isDST: tm.isdst > 0,
+        tzname: if local:
+            if tm.isdst > 0:
+              getTzname().DST
+            else:
+              getTzname().nonDST
           else:
-            getTzname().nonDST
-        else:
-          "UTC",
-      timezone: if local: getTimezone() else: 0
-    )
+            "UTC",
+        # BSD stores in `gmtoff` offset east of UTC in seconds,
+        # but posix systems using west of UTC in seconds
+        timezone: if local: -(tm.gmtoff) else: 0
+      )
+    else:
+      TimeInfo(second: int(tm.second),
+        minute: int(tm.minute),
+        hour: int(tm.hour),
+        monthday: int(tm.monthday),
+        month: Month(tm.month),
+        year: tm.year + 1900'i32,
+        weekday: weekDays[int(tm.weekday)],
+        yearday: int(tm.yearday),
+        isDST: tm.isdst > 0,
+        tzname: if local:
+            if tm.isdst > 0:
+              getTzname().DST
+            else:
+              getTzname().nonDST
+          else:
+            "UTC",
+        timezone: if local: getTimezone() else: 0
+      )
+
 
   proc timeInfoToTM(t: TimeInfo): StructTM =
     const
@@ -564,7 +603,14 @@ when not defined(JS):
     return ($tzname[0], $tzname[1])
 
   proc getTimezone(): int =
-    return timezone
+    when defined(freebsd) or defined(netbsd) or defined(openbsd):
+      var a = timec(nil)
+      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 fromSeconds(since1970: float): Time = Time(since1970)
 
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 45f52eb7f..eeb1b607d 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -14,7 +14,7 @@
 include "system/inclrtl"
 
 type
-  RuneImpl = int # underlying type of Rune
+  RuneImpl = int32 # underlying type of Rune
   Rune* = distinct RuneImpl   ## type that can hold any Unicode character
   Rune16* = distinct int16 ## 16 bit Unicode character
 
@@ -1148,7 +1148,7 @@ const
     0x01f1, 501,  #
     0x01f3, 499]  #
 
-proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int =
+proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
   var n = len
   var t = 0
   while n > 1:
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index aca9d51e2..b83ec44ca 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -310,7 +310,7 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
   ##
   ## .. code-block:: nim
   ##
-  ##  import math
+  ##  import math, random
   ##  proc defectiveRobot() =
   ##    randomize()
   ##    case random(1..4)
diff --git a/lib/system.nim b/lib/system.nim
index f584f7590..3a5415b87 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -588,6 +588,9 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
   ## 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).
   ##
+  ## Limitations: If used within nim VM context ``sizeof`` will only work
+  ## for simple types.
+  ##
   ## .. code-block:: nim
   ##  sizeof('A') #=> 1
   ##  sizeof(2) #=> 8
@@ -2293,12 +2296,15 @@ proc `$`*[T: tuple|object](x: T): string =
     if not firstElement: result.add(", ")
     result.add(name)
     result.add(": ")
-    when compiles(value.isNil):
-      if value.isNil: result.add "nil"
-      else: result.add($value)
+    when compiles($value):
+      when compiles(value.isNil):
+        if value.isNil: result.add "nil"
+        else: result.add($value)
+      else:
+        result.add($value)
+      firstElement = false
     else:
-      result.add($value)
-    firstElement = false
+      result.add("...")
   result.add(")")
 
 proc collectionToString[T: set | seq](x: T, b, e: string): string =
@@ -2511,7 +2517,7 @@ template newException*(exceptn: typedesc, message: string): expr =
   e
 
 when hostOS == "standalone":
-  include panicoverride
+  include "$projectpath/panicoverride"
 
 when not declared(sysFatal):
   when hostOS == "standalone":
@@ -2625,6 +2631,14 @@ when not defined(JS): #and not defined(nimscript):
       else: result = 0
 
   when defined(nimscript):
+    proc readFile*(filename: string): string {.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.
@@ -3031,34 +3045,6 @@ when not defined(JS): #and not defined(nimscript):
   {.pop.} # stacktrace
 
   when not defined(nimscript):
-    proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
-      ## Hints the optimizer that `val` is likely going to be true.
-      ##
-      ## 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 likely(value <= 100):
-      ##       process(value)
-      ##     else:
-      ##       echo "Value too big!"
-
-    proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
-      ## 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)
-
     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.
@@ -3126,6 +3112,58 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
 {.pop.} # checks
 {.pop.} # hints
 
+when not defined(JS):
+  proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
+  proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
+
+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)
+
+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
+  else:
+    when defined(JS):
+      val
+    else:
+      unlikely_proc(val)
+
 proc `/`*(x, y: int): float {.inline, noSideEffect.} =
   ## integer division that results in a float.
   result = toFloat(x) / toFloat(y)
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index e0fd53b7b..00a16e2bb 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -27,15 +27,14 @@ const
 
 type
   PTrunk = ptr Trunk
-  Trunk {.final.} = object
+  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
 
   TrunkBuckets = array[0..255, PTrunk]
-  IntSet {.final.} = object
+  IntSet = object
     data: TrunkBuckets
-{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkBuckets: TrunkBuckets].}
 
 type
   AlignType = BiggestFloat
@@ -64,8 +63,6 @@ type
     next, prev: PBigChunk    # chunks of the same (or bigger) size
     align: int
     data: AlignType      # start of usable memory
-{.deprecated: [TAlignType: AlignType, TFreeCell: FreeCell, TBaseChunk: BaseChunk,
-              TBigChunk: BigChunk, TSmallChunk: SmallChunk].}
 
 template smallChunkOverhead(): expr = sizeof(SmallChunk)-sizeof(AlignType)
 template bigChunkOverhead(): expr = sizeof(BigChunk)-sizeof(AlignType)
@@ -79,18 +76,18 @@ template bigChunkOverhead(): expr = sizeof(BigChunk)-sizeof(AlignType)
 
 type
   PLLChunk = ptr LLChunk
-  LLChunk {.pure.} = object ## *low-level* chunk
+  LLChunk = object ## *low-level* chunk
     size: int                # remaining size
     acc: int                 # accumulator
     next: PLLChunk           # next low-level chunk; only needed for dealloc
 
   PAvlNode = ptr AvlNode
-  AvlNode {.pure, final.} = object
+  AvlNode = object
     link: array[0..1, PAvlNode] # Left (0) and right (1) links
     key, upperBound: int
     level: int
 
-  MemRegion {.final, pure.} = object
+  MemRegion = object
     minLargeObj, maxLargeObj: int
     freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
     llmem: PLLChunk
@@ -99,6 +96,7 @@ type
     freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
     chunkStarts: IntSet
     root, deleted, last, freeAvlNodes: PAvlNode
+    locked: bool # if locked, we cannot free pages.
 {.deprecated: [TLLChunk: LLChunk, TAvlNode: AvlNode, TMemRegion: MemRegion].}
 
 # shared:
@@ -234,7 +232,8 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
 proc chunkUnused(c: PChunk): bool {.inline.} =
   result = not c.used
 
-iterator allObjects(m: MemRegion): pointer {.inline.} =
+iterator allObjects(m: var MemRegion): pointer {.inline.} =
+  m.locked = true
   for s in elements(m.chunkStarts):
     # we need to check here again as it could have been modified:
     if s in m.chunkStarts:
@@ -252,6 +251,7 @@ iterator allObjects(m: MemRegion): pointer {.inline.} =
         else:
           let c = cast[PBigChunk](c)
           yield addr(c.data)
+  m.locked = false
 
 proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
                       magic: "Plugin", compileTime.}
@@ -385,7 +385,7 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
           excl(a.chunkStarts, pageIndex(c))
           c = cast[PBigChunk](le)
 
-  if c.size < ChunkOsReturn or doNotUnmap:
+  if c.size < ChunkOsReturn or doNotUnmap or a.locked:
     incl(a, a.chunkStarts, pageIndex(c))
     updatePrevSize(a, c, c.size)
     listAdd(a.freeChunksList, c)
@@ -442,26 +442,29 @@ proc getSmallChunk(a: var MemRegion): PSmallChunk =
 # -----------------------------------------------------------------------------
 proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
 
-proc allocInv(a: MemRegion): bool =
-  ## checks some (not all yet) invariants of the allocator's data structures.
-  for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
-    var c = a.freeSmallChunks[s]
-    while not (c == nil):
-      if c.next == c:
-        echo "[SYSASSERT] c.next == c"
-        return false
-      if not (c.size == s * MemAlign):
-        echo "[SYSASSERT] c.size != s * MemAlign"
-        return false
-      var it = c.freeList
-      while not (it == nil):
-        if not (it.zeroField == 0):
-          echo "[SYSASSERT] it.zeroField != 0"
-          c_printf("%ld %p\n", it.zeroField, it)
+when true:
+  template allocInv(a: MemRegion): bool = true
+else:
+  proc allocInv(a: MemRegion): bool =
+    ## checks some (not all yet) invariants of the allocator's data structures.
+    for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
+      var c = a.freeSmallChunks[s]
+      while not (c == nil):
+        if c.next == c:
+          echo "[SYSASSERT] c.next == c"
           return false
-        it = it.next
-      c = c.next
-  result = true
+        if not (c.size == s * MemAlign):
+          echo "[SYSASSERT] c.size != s * MemAlign"
+          return false
+        var it = c.freeList
+        while not (it == nil):
+          if not (it.zeroField == 0):
+            echo "[SYSASSERT] it.zeroField != 0"
+            c_printf("%ld %p\n", it.zeroField, it)
+            return false
+          it = it.next
+        c = c.next
+    result = true
 
 proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
   sysAssert(allocInv(a), "rawAlloc: begin")
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 5c2170a17..ee5eec30b 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2016 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,13 +9,8 @@
 
 #            Garbage Collector
 #
-# The basic algorithm is *Deferred Reference Counting* with cycle detection.
-# This is achieved by combining a Deutsch-Bobrow garbage collector
-# together with Christoper's partial mark-sweep garbage collector.
-#
-# Special care has been taken to avoid recursion as far as possible to avoid
-# stack overflows when traversing deep datastructures. It is well-suited
-# for soft real time applications (like games).
+# Refcounting + Mark&Sweep. Complex algorithms avoided.
+# Been there, done that, didn't work.
 
 when defined(nimCoroutines):
   import arch
@@ -30,7 +25,7 @@ const
                       # this seems to be a good value
   withRealTime = defined(useRealtimeGC)
   useMarkForDebug = defined(gcGenerational)
-  useBackupGc = false                     # use a simple M&S GC to collect
+  useBackupGc = true                      # use a simple M&S GC to collect
                                           # cycles instead of the complex
                                           # algorithm
 
@@ -55,8 +50,7 @@ type
   WalkOp = enum
     waMarkGlobal,    # part of the backup/debug mark&sweep
     waMarkPrecise,   # part of the backup/debug mark&sweep
-    waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
-    waCollectWhite #, waDebug
+    waZctDecRef, waPush
 
   Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
     # A ref type can have a finalizer that is called before the object's
@@ -87,7 +81,6 @@ type
       idGenerator: int
     zct: CellSeq             # the zero count table
     decStack: CellSeq        # cells in the stack that are to decref again
-    cycleRoots: CellSet
     tempStack: CellSeq       # temporary stack for recursion elimination
     recGcLock: int           # prevent recursion via finalizers; no thread lock
     when withRealTime:
@@ -96,6 +89,7 @@ type
     stat: GcStat
     when useMarkForDebug or useBackupGc:
       marked: CellSet
+      additionalRoots: CellSeq # dummy roots for GC_ref/unref
     when hasThreadSupport:
       toDispose: SharedList[pointer]
 
@@ -136,9 +130,6 @@ 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
@@ -200,14 +191,16 @@ proc prepareDealloc(cell: PCell) =
     (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
     dec(gch.recGcLock)
 
+template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) =
+  when false:
+    for i in 0..gch.decStack.len-1:
+      if gch.decStack.d[i] == c:
+        sysAssert(false, msg)
+
 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 cycleGC:
-    if c.color != rcPurple:
-      c.setColor(rcPurple)
-      incl(gch.cycleRoots, c)
   when hasThreadSupport and hasSharedHeap:
     releaseSys(HeapLock)
 
@@ -224,22 +217,30 @@ proc decRef(c: PCell) {.inline.} =
   gcAssert(c.refcount >=% rcIncrement, "decRef")
   if --c.refcount:
     rtlAddZCT(c)
-  elif canbeCycleRoot(c):
-    # unfortunately this is necessary here too, because a cycle might just
-    # have been broken up and we could recycle it.
-    rtlAddCycleRoot(c)
-    #writeCell("decRef", c)
 
 proc incRef(c: PCell) {.inline.} =
   gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
   c.refcount = c.refcount +% rcIncrement
   # and not colorMask
   #writeCell("incRef", c)
-  if canbeCycleRoot(c):
-    rtlAddCycleRoot(c)
 
-proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
-proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
+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 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)
+  decRef(usrToCell(p))
 
 proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
   ## adds 'p' to the cycle candidate set for the cycle collector. It is
@@ -306,10 +307,10 @@ proc initGC() =
     # init the rt
     init(gch.zct)
     init(gch.tempStack)
-    init(gch.cycleRoots)
     init(gch.decStack)
     when useMarkForDebug or useBackupGc:
       init(gch.marked)
+      init(gch.additionalRoots)
     when hasThreadSupport:
       gch.toDispose = initSharedList[pointer]()
 
@@ -563,7 +564,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
             d[j] = res
             break
           dec(j)
-      if canbeCycleRoot(ol): excl(gch.cycleRoots, ol)
+      beforeDealloc(gch, ol, "growObj stack trash")
       rawDealloc(gch.region, ol)
     else:
       # we split the old refcount in 2 parts. XXX This is still not entirely
@@ -597,54 +598,12 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) =
   when logGC: writeCell("cycle collector dealloc cell", c)
   when reallyDealloc:
     sysAssert(allocInv(gch.region), "free cyclic cell")
+    beforeDealloc(gch, c, "freeCyclicCell: stack trash")
     rawDealloc(gch.region, c)
   else:
     gcAssert(c.typ != nil, "freeCyclicCell")
     zeroMem(c, sizeof(Cell))
 
-proc markGray(s: PCell) =
-  if s.color != rcGray:
-    setColor(s, rcGray)
-    forAllChildren(s, waMarkGray)
-
-proc scanBlack(s: PCell) =
-  s.setColor(rcBlack)
-  forAllChildren(s, waScanBlack)
-
-proc scan(s: PCell) =
-  if s.color == rcGray:
-    if s.refcount >=% rcIncrement:
-      scanBlack(s)
-    else:
-      s.setColor(rcWhite)
-      forAllChildren(s, waScan)
-
-proc collectWhite(s: PCell) =
-  # This is a hacky way to deal with the following problem (bug #1796)
-  # Consider this content in cycleRoots:
-  #   x -> a; y -> a  where 'a' is an acyclic object so not included in
-  # cycleRoots itself. Then 'collectWhite' used to free 'a' twice. The
-  # 'isAllocatedPtr' check prevents this. This also means we do not need
-  # to query 's notin gch.cycleRoots' at all.
-  if isAllocatedPtr(gch.region, s) and s.color == rcWhite:
-    s.setColor(rcBlack)
-    forAllChildren(s, waCollectWhite)
-    freeCyclicCell(gch, s)
-
-proc markRoots(gch: var GcHeap) =
-  var tabSize = 0
-  for s in elements(gch.cycleRoots):
-    #writeCell("markRoot", s)
-    inc tabSize
-    if s.color == rcPurple and s.refcount >=% rcIncrement:
-      markGray(s)
-    else:
-      excl(gch.cycleRoots, s)
-      # (s.color == rcBlack and rc == 0) as 1 condition:
-      if s.refcount == 0:
-        freeCyclicCell(gch, s)
-  gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
-
 when useBackupGc:
   proc sweep(gch: var GcHeap) =
     for x in allObjects(gch.region):
@@ -666,16 +625,8 @@ when useMarkForDebug or useBackupGc:
 
   proc markGlobals(gch: var GcHeap) =
     for i in 0 .. < globalMarkersLen: globalMarkers[i]()
-
-  proc stackMarkS(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)
-    if c >% PageSize:
-      # fast check: does it look like a cell?
-      var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
-      if objStart != nil:
-        markS(gch, objStart)
+    let d = gch.additionalRoots.d
+    for i in 0 .. < gch.additionalRoots.len: markS(gch, d[i])
 
 when logGC:
   var
@@ -717,19 +668,6 @@ proc doOperation(p: pointer, op: WalkOp) =
     #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
   of waPush:
     add(gch.tempStack, c)
-  of waCycleDecRef:
-    gcAssert(c.refcount >=% rcIncrement, "doOperation 3")
-    c.refcount = c.refcount -% rcIncrement
-  of waMarkGray:
-    gcAssert(c.refcount >=% rcIncrement, "waMarkGray")
-    c.refcount = c.refcount -% rcIncrement
-    markGray(c)
-  of waScan: scan(c)
-  of waScanBlack:
-    c.refcount = c.refcount +% rcIncrement
-    if c.color != rcBlack:
-      scanBlack(c)
-  of waCollectWhite: collectWhite(c)
   of waMarkGlobal:
     when useMarkForDebug or useBackupGc:
       when hasThreadSupport:
@@ -748,14 +686,6 @@ proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
 
 proc collectZCT(gch: var GcHeap): bool {.benign.}
 
-when useMarkForDebug or useBackupGc:
-  proc markStackAndRegistersForSweep(gch: var GcHeap) {.noinline, cdecl,
-                                                         benign.}
-
-proc collectRoots(gch: var GcHeap) =
-  for s in elements(gch.cycleRoots):
-    collectWhite(s)
-
 proc collectCycles(gch: var GcHeap) =
   when hasThreadSupport:
     for c in gch.toDispose:
@@ -764,33 +694,12 @@ proc collectCycles(gch: var GcHeap) =
   while gch.zct.len > 0: discard collectZCT(gch)
   when useBackupGc:
     cellsetReset(gch.marked)
-    markStackAndRegistersForSweep(gch)
-    markGlobals(gch)
-    sweep(gch)
-  else:
-    markRoots(gch)
-    # scanRoots:
-    for s in elements(gch.cycleRoots): scan(s)
-    collectRoots(gch)
-
-    cellsetReset(gch.cycleRoots)
-  # alive cycles need to be kept in 'cycleRoots' if they are referenced
-  # from the stack; otherwise the write barrier will add the cycle root again
-  # anyway:
-  when false:
     var d = gch.decStack.d
-    var cycleRootsLen = 0
     for i in 0..gch.decStack.len-1:
-      var c = d[i]
-      gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
-      gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
-      if canBeCycleRoot(c):
-        #if c notin gch.cycleRoots:
-        inc cycleRootsLen
-        incl(gch.cycleRoots, c)
-      gcAssert c.typ != nil, "addBackStackRoots 2"
-    if cycleRootsLen != 0:
-      cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen)
+      sysAssert isAllocatedPtr(gch.region, d[i]), "collectCycles"
+      markS(gch, d[i])
+    markGlobals(gch)
+    sweep(gch)
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
@@ -812,31 +721,11 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
         add(gch.decStack, cell)
   sysAssert(allocInv(gch.region), "gcMark end")
 
-proc markThreadStacks(gch: var GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    {.error: "not fully implemented".}
-    var it = threadList
-    while it != nil:
-      # mark registers:
-      for i in 0 .. high(it.registers): gcMark(gch, it.registers[i])
-      var sp = cast[ByteAddress](it.stackBottom)
-      var max = cast[ByteAddress](it.stackTop)
-      # XXX stack direction?
-      # XXX unroll this loop:
-      while sp <=% max:
-        gcMark(gch, cast[ppointer](sp)[])
-        sp = sp +% sizeof(pointer)
-      it = it.next
-
 include gc_common
 
 proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
   forEachStackSlot(gch, gcMark)
 
-when useMarkForDebug or useBackupGc:
-  proc markStackAndRegistersForSweep(gch: var GcHeap) =
-    forEachStackSlot(gch, stackMarkS)
-
 proc collectZCT(gch: var GcHeap): bool =
   # Note: Freeing may add child objects to the ZCT! So essentially we do
   # deep freeing, which is bad for incremental operation. In order to
@@ -866,8 +755,6 @@ 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 cycleGC:
-        if canbeCycleRoot(c): excl(gch.cycleRoots, c)
       when logGC: writeCell("zct dealloc cell", c)
       gcTrace(c, csZctFreed)
       # We are about to free the object, call the finalizer BEFORE its
@@ -877,6 +764,7 @@ proc collectZCT(gch: var GcHeap): bool =
       forAllChildren(c, waZctDecRef)
       when reallyDealloc:
         sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
+        beforeDealloc(gch, c, "collectZCT: stack trash")
         rawDealloc(gch.region, c)
       else:
         sysAssert(c.typ != nil, "collectZCT 2")
@@ -915,7 +803,6 @@ proc collectCTBody(gch: var GcHeap) =
   sysAssert(gch.decStack.len == 0, "collectCT")
   prepareForInteriorPointerChecking(gch.region)
   markStackAndRegisters(gch)
-  markThreadStacks(gch)
   gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
   inc(gch.stat.stackScans)
   if collectZCT(gch):
@@ -937,11 +824,6 @@ proc collectCTBody(gch: var GcHeap) =
       if gch.maxPause > 0 and duration > gch.maxPause:
         c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
 
-when useMarkForDebug or useBackupGc:
-  proc markForDebug(gch: var GcHeap) =
-    markStackAndRegistersForSweep(gch)
-    markGlobals(gch)
-
 when defined(nimCoroutines):
   proc currentStackSizes(): int =
     for stack in items(gch.stack):
@@ -1035,7 +917,7 @@ when not defined(useNimRtl):
              "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
              "[GC] zct capacity: " & $gch.zct.cap & "\n" &
              "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
-             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
+             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
     when defined(nimCoroutines):
       result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 7d54c07be..7f8e466b5 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -97,6 +97,8 @@ type
     additionalRoots: CellSeq # dummy roots for GC_ref/unref
     spaceIter: ObjectSpaceIter
     dumpHeapFile: File # File that is used for GC_dumpHeap
+    when hasThreadSupport:
+      toDispose: SharedList[pointer]
 
 var
   gch {.rtlThreadVar.}: GcHeap
@@ -119,6 +121,8 @@ proc initGC() =
     init(gch.decStack)
     init(gch.additionalRoots)
     init(gch.greyStack)
+    when hasThreadSupport:
+      gch.toDispose = initSharedList[pointer]()
 
 # 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
@@ -800,6 +804,10 @@ proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
 proc collectZCT(gch: var GcHeap): bool {.benign.}
 
 proc collectCycles(gch: var GcHeap): bool =
+  when hasThreadSupport:
+    for c in gch.toDispose:
+      nimGCunref(c)
+
   # ensure the ZCT 'color' is not used:
   while gch.zct.len > 0: discard collectZCT(gch)
 
diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim
index c251a4d0b..e30d0a720 100644
--- a/lib/system/gc_stack.nim
+++ b/lib/system/gc_stack.nim
@@ -8,7 +8,23 @@
 
 # "Stack GC" for embedded devices or ultra performance requirements.
 
-include osalloc
+when defined(nimphpext):
+  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 osAllocPages(size: int): pointer {.inline.} =
+    emalloc(size)
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    emalloc(size)
+
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
+    efree(p)
+
+else:
+  include osalloc
 
 # We manage memory as a thread local stack. Since the allocation pointer
 # is detached from the control flow pointer, this model is vastly more
@@ -100,6 +116,7 @@ proc allocSlowPath(r: var MemRegion; size: int) =
   fresh.size = s
   fresh.head = nil
   fresh.tail = nil
+  fresh.next = nil
   inc r.totalSize, s
   let old = r.tail
   if old == nil:
@@ -127,11 +144,12 @@ proc runFinalizers(c: Chunk) =
       (cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
     it = it.nextFinal
 
-proc dealloc(r: var MemRegion; p: pointer) =
-  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
+when false:
+  proc dealloc(r: var MemRegion; p: pointer) =
+    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
 
 proc deallocAll(r: var MemRegion; head: Chunk) =
   var it = head
@@ -168,6 +186,9 @@ proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
 
 proc obstackPtr*(): StackPtr = tlRegion.obstackPtr()
 proc setObstackPtr*(sp: StackPtr) = tlRegion.setObstackPtr(sp)
+proc deallocAll*() = tlRegion.deallocAll()
+
+proc deallocOsPages(r: var MemRegion) = r.deallocAll()
 
 proc joinRegion*(dest: var MemRegion; src: MemRegion) =
   # merging is not hard.
@@ -406,6 +427,13 @@ proc realloc(p: pointer, newsize: Natural): pointer =
   if result == nil: raiseOutOfMem()
 proc dealloc(p: pointer) = cfree(p)
 
+proc alloc0(r: var MemRegion; size: Natural): pointer =
+  # ignore the region. That is correct for the channels module
+  # but incorrect in general. XXX
+  result = alloc0(size)
+
+proc dealloc(r: var MemRegion; p: pointer) = dealloc(p)
+
 proc allocShared(size: Natural): pointer =
   result = cmalloc(size)
   if result == nil: raiseOutOfMem()
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 99997efe6..ce67373bc 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -248,8 +248,12 @@ proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
     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] = `s`[i].toString(16);
+        nonAsciiPart[offset + 1] = code;
       }
       else if (`s`[i] < 128)
         asciiPart[i] = fcc(`s`[i]);
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 78410d716..8b83e194b 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -72,7 +72,7 @@ when defined(emscripten):
 
   proc osTryAllocPages(size: int): pointer = osAllocPages(size)
 
-  proc osDeallocPages(p: pointer, size: int) {.inline} =
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
     var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
     var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
@@ -107,7 +107,7 @@ elif defined(posix):
                              MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
     if result == cast[pointer](-1): result = nil
 
-  proc osDeallocPages(p: pointer, size: int) {.inline} =
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
     when reallyOsDealloc: discard munmap(p, size)
 
 elif defined(windows):
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 3c34215ac..2fcfd69d8 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -236,8 +236,7 @@ when declared(stdout):
 
 # interface to the C procs:
 
-when (defined(windows) and not defined(useWinAnsi)) or defined(nimdoc):
-  include "system/widestrs"
+include "system/widestrs"
 
 when defined(windows) and not defined(useWinAnsi):
   when defined(cpp):
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index f8b93a2c3..74fcfd8c5 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -304,43 +304,39 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
 proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,
   header: "<stdlib.h>", noSideEffect.}
 
-var decimalPoint: char
-
-proc getDecimalPoint(): char =
-  result = decimalPoint
-  if result == '\0':
-    if strtod("0,5", nil) == 0.5: result = ','
-    else: result = '.'
-    # yes this is threadsafe in practice, spare me:
-    decimalPoint = result
-
 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 leverages `strtod()` for the non-trivial task of
-  # parsing floating point numbers correctly. Because `strtod()` is
-  # locale-dependent with respect to the radix character, we create
-  # a copy where the decimal point is replaced with the locale's
-  # radix character.
+  # 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
-    t: array[500, char] # flaviu says: 325 is the longest reasonable literal
-    ti = 0
-    hasdigits = false
-
-  template addToBuf(c) =
-    if ti < t.high:
-      t[ti] = c; inc(ti)
+    kdigits, fdigits = 0
+    exponent: int
+    integer: uint64
+    fraction: 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
-    t[ti] = s[i]
-    inc(i); inc(ti)
+    inc(i)
 
   # NaN?
   if s[i] == 'N' or s[i] == 'n':
@@ -360,40 +356,111 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
           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'}:
-    hasdigits = true
-    addToBuf(s[i])
-    inc(i);
+    inc(kdigits)
+    integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
+    inc(i)
     while s[i] == '_': inc(i)
 
   # Fractional part?
   if s[i] == '.':
-    addToBuf(getDecimalPoint())
     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'}:
-      hasdigits = true
-      addToBuf(s[i])
+      inc(fdigits)
+      inc(frac_exponent)
+      integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
       inc(i)
       while s[i] == '_': inc(i)
-  if not hasdigits:
+
+  # 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
 
-  # Exponent?
   if s[i] in {'e', 'E'}:
-    addToBuf(s[i])
     inc(i)
-    if s[i] in {'+', '-'}:
-      addToBuf(s[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'}:
-      addToBuf(s[i])
+      exponent = exponent * 10 + (ord(s[i]) - ord('0'))
       inc(i)
-      while s[i] == '_': inc(i)
-  number = strtod(t, nil)
+      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
+    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)
+  if kdigits + fdigits <= 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.
+  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
+
+  number = strtod(t, nil)
 
 proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
   result = newString(sizeof(x)*4)
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index bdb737e35..99927fbac 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -301,7 +301,7 @@ type
                                        ## a pointer as a thread ID.
 {.deprecated: [TThread: Thread, TThreadId: ThreadId].}
 
-when not defined(boehmgc) and not hasSharedHeap and not defined(gogc):
+when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcstack):
   proc deallocOsPages()
 
 when defined(boehmgc):
@@ -331,7 +331,7 @@ else:
 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):
+  elif not defined(nogc) and not defined(gogc) and not defined(gcstack):
     var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
       threadProcWrapDispatch[TArg]
     when not hasSharedHeap:
diff --git a/lib/system/timers.nim b/lib/system/timers.nim
index ac8418824..8aa4505c4 100644
--- a/lib/system/timers.nim
+++ b/lib/system/timers.nim
@@ -61,7 +61,7 @@ elif defined(posixRealtime):
                final, pure.} = object ## struct timespec
       tv_sec: int  ## Seconds.
       tv_nsec: int ## Nanoseconds.
-  {.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
+  {.deprecated: [TClockid: Clockid, TTimeSpec: TimeSpec].}
 
   var
     CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 8ad3faf41..645ccd57b 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -94,7 +94,7 @@ type
     dwMinorVersion*: DWORD
     dwBuildNumber*: DWORD
     dwPlatformId*: DWORD
-    szCSDVersion*: array[0..127, WinChar];
+    szCSDVersion*: array[0..127, WinChar]
 
 {.deprecated: [THandle: Handle, TSECURITY_ATTRIBUTES: SECURITY_ATTRIBUTES,
     TSTARTUPINFO: STARTUPINFO, TPROCESS_INFORMATION: PROCESS_INFORMATION,
@@ -518,6 +518,8 @@ var
   SO_DEBUG* {.importc, header: "winsock2.h".}: cint ## turn on debugging info recording
   SO_ACCEPTCONN* {.importc, header: "winsock2.h".}: cint # socket has had listen()
   SO_REUSEADDR* {.importc, header: "winsock2.h".}: cint # allow local address reuse
+  SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "winsock2.h".}: cint # allow port reuse. Since Windows does not really support it, mapped to SO_REUSEADDR. This shouldn't cause problems.
+
   SO_KEEPALIVE* {.importc, header: "winsock2.h".}: cint # keep connections alive
   SO_DONTROUTE* {.importc, header: "winsock2.h".}: cint # just use interface addresses
   SO_BROADCAST* {.importc, header: "winsock2.h".}: cint # permit sending of broadcast msgs
diff --git a/tests/async/tasyncdiscard.nim b/tests/async/tasyncdiscard.nim
index 71aba29e2..e7c87ad42 100644
--- a/tests/async/tasyncdiscard.nim
+++ b/tests/async/tasyncdiscard.nim
@@ -36,4 +36,4 @@ proc main {.async.} =
   discard await g()
   echo 6
 
-asyncCheck main()
+waitFor(main())
diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim
index f77198e2e..5930f296f 100644
--- a/tests/async/tasynctry.nim
+++ b/tests/async/tasynctry.nim
@@ -48,7 +48,7 @@ proc catch() {.async.} =
   except OSError, EInvalidField:
     assert false
 
-asyncCheck catch()
+waitFor catch()
 
 proc test(): Future[bool] {.async.} =
   result = false
@@ -92,13 +92,13 @@ proc test4(): Future[int] {.async.} =
     result = 2
 
 var x = test()
-assert x.read
+assert x.waitFor()
 
 x = test2()
-assert x.read
+assert x.waitFor()
 
 var y = test3()
-assert y.read == 2
+assert y.waitFor() == 2
 
 y = test4()
-assert y.read == 2
+assert y.waitFor() == 2
diff --git a/tests/ccgbugs/twrong_string_asgn.nim b/tests/ccgbugs/twrong_string_asgn.nim
index b62e70e7c..669b7f8f5 100644
--- a/tests/ccgbugs/twrong_string_asgn.nim
+++ b/tests/ccgbugs/twrong_string_asgn.nim
@@ -16,4 +16,4 @@ x.callback =
   proc () =
     finished = true
 
-while not finished: discard
+while not finished: poll()
diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim
index b081212e6..6d9bdd539 100644
--- a/tests/enum/tenum.nim
+++ b/tests/enum/tenum.nim
@@ -6,3 +6,9 @@ type
 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)
diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim
index 960c4e5f7..006b4d88f 100644
--- a/tests/float/tfloat4.nim
+++ b/tests/float/tfloat4.nim
@@ -1,3 +1,8 @@
+discard """
+  file: "tfloat4.nim"
+  output: "passed all tests."
+  exitcode: 0
+"""
 import math, strutils
 
 proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", varargs.}
@@ -11,8 +16,9 @@ proc floatToStr(f: float64): string =
       return
     add(result, ch)
 
+
 let testFloats = [
-  "0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99",
+  "0", "-0", "0.", "0.0", "-0.", "-0.0", "-1", "1", "1.", ".3", "3.3", "-.3", "-99.99",
   "1.1e10", "-2e100", "1.234e-10", "1.234e+10",
   "-inf", "inf", "+inf",
   "3.14159265358979323846264338327950288",
@@ -25,18 +31,20 @@ let testFloats = [
 ]
 
 for num in testFloats:
-  assert num.parseFloat.floatToStr.parseFloat == num.parseFloat
+  doAssert num.parseFloat.floatToStr.parseFloat == num.parseFloat
 
-assert "0".parseFloat == 0.0
-assert "-.1".parseFloat == -0.1
-assert "2.5e1".parseFloat == 25.0
-assert "1e10".parseFloat == 10_000_000_000.0
-assert "0.000_005".parseFloat == 5.000_000e-6
-assert "1.234_567e+2".parseFloat == 123.4567
-assert "1e1_00".parseFloat == "1e100".parseFloat
-assert "3.1415926535897932384626433".parseFloat ==
+doAssert "0".parseFloat == 0.0
+doAssert "-.1".parseFloat == -0.1
+doAssert "2.5e1".parseFloat == 25.0
+doAssert "1e10".parseFloat == 10_000_000_000.0
+doAssert "0.000_005".parseFloat == 5.000_000e-6
+doAssert "1.234_567e+2".parseFloat == 123.4567
+doAssert "1e1_00".parseFloat == "1e100".parseFloat
+doAssert "3.1415926535897932384626433".parseFloat ==
        3.1415926535897932384626433
-assert "2.71828182845904523536028747".parseFloat ==
+doAssert "2.71828182845904523536028747".parseFloat ==
        2.71828182845904523536028747
-assert 0.00097656250000000021684043449710088680149056017398834228515625 ==
+doAssert 0.00097656250000000021684043449710088680149056017398834228515625 ==
      "0.00097656250000000021684043449710088680149056017398834228515625".parseFloat
+
+echo("passed all tests.")
diff --git a/tests/float/tfloat5.nim b/tests/float/tfloat5.nim
new file mode 100644
index 000000000..aa7dc6c53
--- /dev/null
+++ b/tests/float/tfloat5.nim
@@ -0,0 +1,15 @@
+discard """
+  file: "tfloat5.nim"
+  output: '''0 : 0.0
+0 : 0.0
+0 : 0.0
+0 : 0.0'''
+"""
+
+import parseutils
+
+var f: float
+echo "*".parseFloat(f), " : ", f
+echo "/".parseFloat(f), " : ", f
+echo "+".parseFloat(f), " : ", f
+echo "-".parseFloat(f), " : ", f
diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim
new file mode 100644
index 000000000..721abd721
--- /dev/null
+++ b/tests/float/tfloat6.nim
@@ -0,0 +1,21 @@
+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'''
+"""
+
+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
new file mode 100644
index 000000000..2337d1dd4
--- /dev/null
+++ b/tests/float/tfloat7.nim
@@ -0,0 +1,26 @@
+discard """
+  file: "tfloat6.nim"
+  output: '''passed.
+passed.
+passed.
+passed.
+passed.
+passed.
+passed.'''
+"""
+
+import strutils
+template expect_fail(x: expr) =
+  try:
+    discard x
+    echo("expected to fail!")
+  except ValueError:
+    echo("passed.")
+
+expect_fail("1_0._00_0001".parseFloat())
+expect_fail("_1_0_00.0001".parseFloat())
+expect_fail("10.00.01".parseFloat())
+expect_fail("10.00E_01".parseFloat())
+expect_fail("10.00E_01".parseFloat())
+expect_fail("10.00E".parseFloat())
+expect_fail("10.00A".parseFloat())
diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim
new file mode 100644
index 000000000..efab49e36
--- /dev/null
+++ b/tests/gc/thavlak.nim
@@ -0,0 +1,457 @@
+discard """
+  output: '''Welcome to LoopTesterApp, Nim edition
+Constructing Simple CFG...
+15000 dummy loops
+Constructing CFG...
+Performing Loop Recognition
+1 Iteration
+Another 50 iterations...
+..................................................
+Found 1 loops (including artificial root node) (50)'''
+"""
+
+# bug #3184
+
+import tables
+import sequtils
+import sets
+
+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 =
+  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): BasicBlockEdge =
+  result.fr = cfg.createNode(fromName)
+  result.to = cfg.createNode(toName)
+  result.fr.outEdges.add(result.to)
+  result.to.inEdges.add(result.fr)
+  cfg.addEdge(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) =
+  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 =
+  result = newSimpleLoop()
+  loop_counter += 1
+  result.counter = loop_counter
+
+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)
+  when false:
+    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]()
+  result = self
+
+  while result != result.parent:
+    var parent = result.parent
+    if parent != parent.parent: nodeList.add result
+    result = parent
+
+  for iter in nodeList: iter.parent = result.parent
+
+proc union(self: ref UnionFindNode, unionFindNode: ref 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
+
+  # # Marker for uninitialized nodes.
+  UNVISITED = -1
+
+  # # Safeguard against pathologic algorithm behavior.
+  MAXNONBACKPREDS = (32 * 1024)
+
+type
+  HavlakLoopFinder = object
+    cfg: Cfg
+    lsg: Lsg
+
+proc newHavlakLoopFinder(cfg: Cfg, lsg: 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 =
+  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))
+        #result = dfs(target, nodes, number, last, result + 1)
+  last[number[currentNode]] = result
+
+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 initSet[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
+  var res = 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
+  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
+
+proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) =
+  var x1 = 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) =
+  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)
+  self.buildConnect(0, 2)
+
+  echo "15000 dummy loops"
+
+  for i in 1..15000:
+    var h = newHavlakLoopFinder(self.cfg, newLsg())
+    var res = 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)
+
+  echo "Performing Loop Recognition\n1 Iteration"
+
+  var h = newHavlakLoopFinder(self.cfg, newLsg())
+  var loops = h.findLoops
+
+  echo "Another 50 iterations..."
+
+  var sum = 0
+  for i in 1..50:
+    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
diff --git a/tests/gc/tlists.nim b/tests/gc/tlists.nim
new file mode 100644
index 000000000..26b32396c
--- /dev/null
+++ b/tests/gc/tlists.nim
@@ -0,0 +1,37 @@
+discard """
+    output: '''Success'''
+"""
+
+# bug #3793
+
+import os
+import math
+import lists
+import strutils
+
+proc mkleak() =
+    # allocate 10 MB via linked lists
+    let numberOfLists = 100
+    for i in countUp(1, numberOfLists):
+        var leakList = initDoublyLinkedList[string]()
+        let numberOfLeaks = 50000
+        for j in countUp(1, numberOfLeaks):
+            let leakSize = 200
+            let leaked = newString(leakSize)
+            leakList.append(leaked)
+
+proc mkManyLeaks() =
+    for i in 0..0:
+        when false: echo getOccupiedMem()
+        mkleak()
+        when false: echo getOccupiedMem()
+        # Force a full collection. This should free all of the
+        # 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:
+          echo GC_getStatistics()
+          quit "leaking"
+    echo "Success"
+
+mkManyLeaks()
diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim
new file mode 100644
index 000000000..0ecaf9ded
--- /dev/null
+++ b/tests/generics/tgenerictmpl2.nim
@@ -0,0 +1,31 @@
+discard """
+  output: '''1
+1
+1
+1
+999
+999
+999
+2'''
+"""
+
+# test if we can pass explicit generic arguments to generic templates
+# based on bug report #3496
+
+proc     tproc[T](t: T = 999) = echo t
+template ttmpl[T](t: T = 999) = echo t
+
+tproc(1)
+tproc[int](1)
+ttmpl(1)
+ttmpl[int](1) #<- crash case #1
+
+tproc[int]()
+discard tproc[int]
+ttmpl[int]()  #<- crash case #2
+ttmpl[int]    #<- crash case #3
+
+# but still allow normal use of [] on non-generic templates
+
+template tarr: expr = [1, 2, 3, 4]
+echo tarr[1]
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
index c0d93814c..0ec4f4743 100644
--- a/tests/js/tclosures.nim
+++ b/tests/js/tclosures.nim
@@ -2,7 +2,7 @@ discard """
   action: run
 """
 
-import math, strutils
+import math, random, strutils
 const consolePrefix = "jsCallbacks"
 
 asm """
diff --git a/tests/js/test2.nim b/tests/js/test2.nim
index f6976d058..0f460d6f8 100644
--- a/tests/js/test2.nim
+++ b/tests/js/test2.nim
@@ -1,7 +1,8 @@
 discard """
   output: '''foo
 js 3.14
-7'''
+7
+1'''
 """
 
 # This file tests the JavaScript generator
@@ -29,3 +30,13 @@ proc test(x: C, T: typedesc): T =
   cast[T](x)
 
 echo 7.test(int8)
+
+# #4222
+const someConst = [ "1"]
+
+proc procThatRefersToConst() # Forward decl
+procThatRefersToConst() # Call bar before it is defined
+
+proc procThatRefersToConst() =
+  var i = 0 # Use a var index, otherwise nim will constfold foo[0]
+  echo someConst[i] # JS exception here: foo is still not initialized (undefined)
diff --git a/tests/js/testtojsstr.nim b/tests/js/testtojsstr.nim
new file mode 100644
index 000000000..03ac89e20
--- /dev/null
+++ b/tests/js/testtojsstr.nim
@@ -0,0 +1,8 @@
+discard """
+  output = "И\n"
+"""
+
+let s: string = "И\n"
+let cs = s.cstring
+
+echo $s
diff --git a/tests/lexer/tstrlits.nim b/tests/lexer/tstrlits.nim
index f5b7ce937..cc8872f60 100644
--- a/tests/lexer/tstrlits.nim
+++ b/tests/lexer/tstrlits.nim
@@ -1,6 +1,6 @@
 discard """
   file: "tstrlits.nim"
-  output: "a\"\"long string\"\"\"\"\"abc\"def"
+  output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●"
 """
 # Test the new different string literals
 
@@ -11,9 +11,13 @@ const
 
   raw = r"abc""def"
 
+  escaped = "\x5f'\50'\u25cf"
+
+
 stdout.write(rawQuote)
 stdout.write(tripleEmpty)
 stdout.write(raw)
+stdout.write(escaped)
 #OUT a""long string"""""abc"def
 
 
diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim
new file mode 100644
index 000000000..22e03a119
--- /dev/null
+++ b/tests/macros/tgettypeinst.nim
@@ -0,0 +1,122 @@
+discard """
+"""
+
+import macros, strUtils
+
+proc symToIdent(x: NimNode): NimNode =
+  case x.kind:
+    of nnkCharLit..nnkUInt64Lit:
+      result = newNimNode(x.kind)
+      result.intVal = x.intVal
+    of nnkFloatLit..nnkFloat64Lit:
+      result = newNimNode(x.kind)
+      result.floatVal = x.floatVal
+    of nnkStrLit..nnkTripleStrLit:
+      result = newNimNode(x.kind)
+      result.strVal = x.strVal
+    of nnkIdent, nnkSym:
+      result = newIdentNode($x)
+    else:
+      result = newNimNode(x.kind)
+      for c in x:
+        result.add symToIdent(c)
+
+macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
+  let inst = x.getTypeInst
+  let impl = x.getTypeImpl
+  let inst0r = inst0.symToIdent.treeRepr
+  let instr = inst.symToIdent.treeRepr
+  #echo inst0r
+  #echo instr
+  doAssert(instr == inst0r)
+  var impl0 =
+    if implX.kind == nnkNilLit: inst0
+    else: implX[0][2]
+  let impl0r = impl0.symToIdent.treerepr
+  let implr = impl.symToIdent.treerepr
+  #echo impl0r
+  #echo implr
+  doAssert(implr == impl0r)
+  template echoString(s:string) = echo s.replace("\n","\n  ")
+  result = newStmtList()
+  #result.add getAst(echoString("  " & inst0.repr))
+  #result.add getAst(echoString("  " & inst.repr))
+  #result.add getAst(echoString("  " & impl0.repr))
+  #result.add getAst(echoString("  " & impl.repr))
+  if recurse:
+    template testDecl(n, m :typed) =
+      testV(n, false):
+        type _ = m
+    result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
+
+template testV(inst, recurse, impl) =
+  block:
+    #echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
+    var x: inst
+    testX(x, inst, recurse, impl)
+template testT(inst, recurse) =
+  block:
+    type myType = inst
+    testV(myType, recurse):
+      type _ = inst
+
+template test(inst) =
+  testT(inst, false)
+  testV(inst, true, nil)
+template test(inst, impl) = testV(inst, true, impl)
+
+type
+  Model = object of RootObj
+  User = object of Model
+    name : string
+    password : string
+
+  Tree = object of RootObj
+    value : int
+    left,right : ref Tree
+
+  MyEnum = enum
+    valueA, valueB, valueC
+
+  MySet = set[MyEnum]
+  MySeq = seq[int]
+  MyIntPtr = ptr int
+  MyIntRef = ref int
+
+  GenericObject[T] = object
+    value:T
+  Foo[N:static[int],T] = object
+  Bar[N:static[int],T] = object
+    #baz:Foo[N+1,GenericObject[T]]
+    baz:Foo[N,GenericObject[T]]
+
+test(bool)
+test(char)
+test(int)
+test(float)
+test(ptr int)
+test(ref int)
+test(array[1..10,Bar[2,Foo[3,float]]])
+test(distinct Bar[2,Foo[3,float]])
+test(tuple[a:int,b:Foo[-1,float]])
+#test(MyEnum):
+#  type _ = enum
+#    valueA, valueB, valueC
+test(set[MyEnum])
+test(seq[int])
+test(Bar[2,Foo[3,float]]):
+  type _ = object
+    baz: Foo[2, GenericObject[Foo[3, float]]]
+test(Model):
+  type _ = object of RootObj
+test(User):
+  type _ = object of Model
+    name: string
+    password: string
+test(Tree):
+  type _ = object of RootObj
+    value: int
+    left: ref Tree
+    right: ref Tree
+test(proc (a: int, b: Foo[2,float]))
+test(proc (a: int, b: Foo[2,float]): Bar[3,int])
diff --git a/tests/manyloc/keineschweine/lib/vehicles.nim b/tests/manyloc/keineschweine/lib/vehicles.nim
index 94ebf9f57..ddfb43b38 100644
--- a/tests/manyloc/keineschweine/lib/vehicles.nim
+++ b/tests/manyloc/keineschweine/lib/vehicles.nim
@@ -1,6 +1,6 @@
 import
   sfml, chipmunk,
-  sg_assets, sfml_stuff, keineschweine
+  sg_assets, sfml_stuff, "../keineschweine"
 
 
 proc accel*(obj: PVehicle, dt: float) =
diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index e91b86986..2055d7834 100644
--- a/tests/manyloc/nake/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -1,5 +1,5 @@
 import nake
-import httpclient, zip/zipfiles, times, math, sequtils
+import httpclient, zip/zipfiles, times, random, sequtils
 nakeImports
 
 randomize()
@@ -145,7 +145,7 @@ task "download", "download game assets":
     echo "Extracted the libs dir. Copy the ones you need to this dir."
 
 task "zip-lib", "zip up the libs dir":
-  var z: TZipArchive
+  var z: ZipArchive
   if not z.open("libs-" & getDateStr() & ".zip", fmReadWrite):
     quit "Could not open zip"
   for file in walkDirRec("libs", {pcFile, pcDir}):
diff --git a/tests/manyloc/named_argument_bug/main.nim.cfg b/tests/manyloc/named_argument_bug/main.nim.cfg
index 27cf8e688..7df7a0e97 100644
--- a/tests/manyloc/named_argument_bug/main.nim.cfg
+++ b/tests/manyloc/named_argument_bug/main.nim.cfg
@@ -1,2 +1,3 @@
 # this file only exists to mark 'main.nim' as the main file
 
+--path:"$projectpath"
diff --git a/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim b/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim
index 7e7517998..b95cfa379 100644
--- a/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim
+++ b/tests/manyloc/named_argument_bug/tri_engine/math/circle.nim
@@ -1,6 +1,6 @@
 import
-  tri_engine/config,
-  tri_engine/math/vec
+  ../config,
+  vec
 
 type
   TCircle* = tuple[p: TV2[TR], r: TR]
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 3b57acb8e..926958fe4 100644
--- a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
+++ b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
@@ -1,6 +1,6 @@
 import
   macros,
-  tri_engine/config
+  "../config"
 
 type
   TV2*[T:SomeNumber=TR] = array[0..1, T]
diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim
index f1944b637..75584bcab 100644
--- a/tests/overload/tstmtoverload.nim
+++ b/tests/overload/tstmtoverload.nim
@@ -10,7 +10,7 @@ template test(loopCount: int, extraI: int, testBody: stmt): stmt =
 
 template test(loopCount: int, extraF: float, testBody: stmt): stmt =
   block:
-    test(loopCount, round(extraF), testBody)
+    test(loopCount, round(extraF).int, testBody)
 
 template test(loopCount: int, testBody: stmt): stmt =
   block:
diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim
index db32a96d8..57e0588a0 100644
--- a/tests/parallel/twrong_refcounts.nim
+++ b/tests/parallel/twrong_refcounts.nim
@@ -2,7 +2,7 @@ discard """
   output: "Success"
 """
 
-import math, threadPool
+import math, random, threadPool
 
 # ---
 
diff --git a/tests/parser/tdo.nim b/tests/parser/tdo.nim
new file mode 100644
index 000000000..7bd1f7411
--- /dev/null
+++ b/tests/parser/tdo.nim
@@ -0,0 +1,79 @@
+discard """
+  output: '''true
+true
+true
+true inner B'''
+"""
+
+template withValue(a, b, c, d, e: untyped) =
+  if c:
+    d
+  else:
+    e
+
+template withValue(a, b, c, d: untyped) =
+  if c:
+    d
+
+const
+  EVENT_READ = 1
+  EVENT_WRITE = 2
+  FLAG_HANDLE = 3
+  EVENT_MASK = 3
+
+var s: string
+
+proc main =
+  var value = false
+  var fd = 8888
+  var event = 0
+  s.withValue(fd, value) do:
+    if value:
+      var oe = (EVENT_MASK)
+      if (oe xor event) != 0:
+        if (oe and EVENT_READ) != 0 and (event and EVENT_READ) == 0:
+          discard
+        if (oe and EVENT_WRITE) != 0 and (event and EVENT_WRITE) == 0:
+          discard
+        if (oe and EVENT_READ) == 0 and (event and EVENT_READ) != 0:
+          discard
+        if (oe and EVENT_WRITE) == 0 and (event and EVENT_WRITE) != 0:
+          discard
+    else:
+      raise newException(ValueError, "error")
+  do:
+    raise newException(ValueError, "Descriptor is not registered in queue")
+
+proc main2 =
+  var unused = 8
+  # test 'then' branch:
+  s.withValue(unused, true) do:
+    echo "true"
+  do:
+    echo "false"
+
+  # test overloading:
+  s.withValue(unused, false) do:
+    echo "cannot come here"
+
+  # test 'else' branch:
+  s.withValue(unused, false) do:
+    echo "false"
+  do:
+    echo "true"
+
+  # test proper nesting:
+  s.withValue(unused, false) do:
+    echo "false"
+    s.withValue(unused, false) do:
+      echo "false inner A"
+    do:
+      echo "true inner A"
+  do:
+    echo "true"
+    s.withValue(unused, false) do:
+      echo "false inner B"
+    do:
+      echo "true inner B"
+
+main2()
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 1ac9c8092..538582ba8 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -1,4 +1,4 @@
-import math
+import math, random
 import unittest
 import sets
 
diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim
index 4c11ccf61..7022d071b 100644
--- a/tests/stdlib/tparscfg.nim
+++ b/tests/stdlib/tparscfg.nim
@@ -1,25 +1,37 @@
+import parsecfg
 
-import
-  os, parsecfg, strutils, 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")
+dict1.writeConfig("config.ini")
+
+## Reading a configuration file.
+var dict2 = loadConfig("config.ini")
+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
+
+## Modifying a configuration file.
+var dict3 = loadConfig("config.ini")
+dict3.setSectionKey("Author","name","lhf")
+dict3.writeConfig("config.ini")
+
+## Deleting a section key in a configuration file.
+var dict4 = loadConfig("config.ini")
+dict4.delSectionKey("Author","email")
+dict4.writeConfig("config.ini")
 
-var f = newFileStream(paramStr(1), fmRead)
-if f != nil:
-  var p: TCfgParser
-  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/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index 4b210c23b..73113ac68 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -26,7 +26,7 @@ test "unittest multiple requires":
   require(true)
 
 
-import math
+import math, random
 from strutils import parseInt
 proc defectiveRobot() =
   randomize()
diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim
index b87e3328c..ec364c5f3 100644
--- a/tests/template/t2do.nim
+++ b/tests/template/t2do.nim
@@ -15,8 +15,9 @@ template toFloatHelper(result: expr; tooSmall, tooLarge: stmt) {.immediate.} =
     tooLarge
 
 proc toFloat*(a: int): float =
-  toFloatHelper(result)
-    do: raise newException(ValueError, "number too small"):
-        raise newException(ValueError, "number too large")
+  toFloatHelper(result) do:
+    raise newException(ValueError, "number too small")
+  do:
+    raise newException(ValueError, "number too large")
 
 echo toFloat(8)
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 150c55edc..125643d5a 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -152,6 +152,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
   testWithoutBoehm "closureleak"
   testWithoutMs "refarrayleak"
 
+  testWithoutBoehm "tlists"
+  testWithoutBoehm "thavlak"
+
   test "stackrefleak"
   test "cyclecollector"
 
@@ -223,7 +226,7 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
                    "varres/tvartup", "misc/tints", "misc/tunsignedinc"]:
     test "tests/" & testfile & ".nim"
 
-  for testfile in ["pure/strutils", "pure/json"]:
+  for testfile in ["pure/strutils", "pure/json", "pure/random", "pure/times"]:
     test "lib/" & testfile & ".nim"
 
 # ------------------------- manyloc -------------------------------------------
diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim
index be79fadae..4a98e6c27 100644
--- a/tests/threads/ttryrecv.nim
+++ b/tests/threads/ttryrecv.nim
@@ -4,7 +4,7 @@ discard """
 
 # bug #1816
 
-from math import random
+from random import random
 from os import sleep
 
 type PComm = ptr Channel[int]
diff --git a/tests/trmacros/tstatic_t_bug.nim b/tests/trmacros/tstatic_t_bug.nim
new file mode 100644
index 000000000..cdfa53514
--- /dev/null
+++ b/tests/trmacros/tstatic_t_bug.nim
@@ -0,0 +1,24 @@
+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/usingstmt/tthis.nim b/tests/usingstmt/tthis.nim
new file mode 100644
index 000000000..83d75d08c
--- /dev/null
+++ b/tests/usingstmt/tthis.nim
@@ -0,0 +1,15 @@
+
+# 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/vm/meta.nim b/tests/vm/meta.nim
new file mode 100644
index 000000000..2aa01b5b3
--- /dev/null
+++ b/tests/vm/meta.nim
@@ -0,0 +1,240 @@
+#
+# meta.nim
+#
+
+import tables
+import macros
+
+type
+  NodeSeq* = seq[NimNode]
+  Ident* = tuple[name: string, exported: bool]
+  Bracket* = seq[Ident]
+  Field* = tuple[identifier: Ident, type_name: string, default: string]
+  FieldSeq* = seq[Field]
+  TypeDef* = object
+    identifier*: Ident
+    fields*: FieldSeq
+    is_ref*: bool
+    object_type*: string
+    base_type*: string
+  TypeDefSeq* = seq[TypeDef]
+  Proc* = tuple[identifier: Ident, params: FieldSeq,
+                returns: Ident, generics: FieldSeq, body: NimNode]
+  ProcSeq* = seq[Proc]
+
+# Ident procs
+proc newIdent*(name: string, exported = false): Ident =
+  result.name = name
+  result.exported = exported
+
+proc newIdent*(node: NimNode): Ident =
+  case node.kind:
+    of nnkPostfix:
+      result = newIdent(node[1])
+      result.exported = true
+    of nnkIdent, nnkSym:
+      result.name = $(node)
+    else:
+      let msg = "newIdent cannot initialize from node kind: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+proc render*(i: Ident): NimNode {.compileTime.} =
+  if i.name == nil:
+    return newNimNode(nnkEmpty)
+
+  if i.exported:
+    result = newNimNode(nnkPostfix)
+    result.add(ident "*")
+    result.add(ident i.name)
+  else:
+    result = ident i.name
+
+proc `$`*(identifier: Ident): string = identifier.name
+
+converter toString*(x: Ident): string = x.name
+
+proc newBracket*(node: NimNode): Bracket =
+  result = @[]
+  case node.kind:
+    of nnkBracket:
+      for child in node:
+        if child.kind != nnkIdent:
+          let msg = "Bracket members can only be nnkIdent not kind: " & $(node.kind)
+          raise newException(ValueError, msg)
+        result.add(newIdent(child))
+    else:
+      let msg = "newBracket must initialize from node kind nnkBracket not: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+# Field procs
+proc newField*(identifier: Ident, type_name: string, default: string = nil): Field =
+  result.identifier = identifier
+  result.type_name = type_name
+  result.default = default
+
+proc newField*(node: NimNode): Field =
+  case node.kind:
+    of nnkIdentDefs:
+      if node.len > 3:
+        let msg = "newField cannot initialize from nnkIdentDefs with multiple names"
+        raise newException(ValueError, msg)
+      result.identifier = newIdent(node[0])
+      result.type_name = $(node[1])
+      case node[2].kind:
+        of nnkIdent:
+          result.default = $(node[2])
+        else:
+          result.default = nil
+    else:
+      let msg = "newField cannot initialize from node kind: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+# FieldSeq procs
+proc newFieldSeq*(node: NimNode): FieldSeq =
+  result = @[]
+  case node.kind:
+    of nnkIdentDefs:
+      let
+        type_name = $(node[node.len - 2])
+        default_node = node[node.len - 1]
+      var default: string
+      case default_node.kind:
+        of nnkIdent:
+          default = $(default_node)
+        else:
+          default = nil
+      for i in 0..node.len - 3:
+        let name = newIdent(node[i])
+        result.add(newField(name, type_name, default))
+    of nnkRecList, nnkVarSection, nnkGenericParams:
+      for child in node:
+        result = result & newFieldSeq(child)
+    else:
+      let msg = "newFieldSeq cannot initialize from node kind: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+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()
+  newIdentDefs(identifier, type_name, default)
+
+proc render*(fs: FieldSeq): NimNode {.compileTime.} =
+  result = newNimNode(nnkRecList)
+  for field in fs:
+    result.add(field.render())
+
+# TypeDef procs
+proc newTypeDef*(identifier: Ident, is_ref = false,
+                object_type = "object",
+                base_type: string = nil): TypeDef {.compileTime.} =
+  result.identifier = identifier
+  result.fields = @[]
+  result.is_ref = is_ref
+  result.object_type = "object"
+  result.base_type = base_type
+
+proc newTypeDef*(node: NimNode): TypeDef {.compileTime.} =
+  case node.kind:
+    of nnkTypeDef:
+      result.identifier = newIdent($(node[0]))
+      var object_node: NimNode
+      case node[2].kind:
+        of nnkRefTy:
+          object_node = node[2][0]
+          result.is_ref = true
+        of nnkObjectTy:
+          object_node = node[2]
+          result.is_ref = false
+        else:
+          let msg = "newTypeDef could not parse RefTy/ObjectTy, found: " & $(node[2].kind)
+          raise newException(ValueError, msg)
+      case object_node[1].kind:
+        of nnkOfInherit:
+          result.base_type = $(object_node[1][0])
+        else:
+          result.base_type = "object"
+      result.fields = newFieldSeq(object_node[2])
+    else:
+      let msg = "newTypeDef cannot initialize from node kind: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+proc render*(typedef: TypeDef): NimNode {.compileTime.} =
+  result = newNimNode(nnkTypeDef)
+  result.add(typedef.identifier.render)
+  result.add(newEmptyNode())
+  let object_node = newNimNode(nnkObjectTy)
+  object_node.add(newEmptyNode())
+  if typedef.base_type == nil:
+    object_node.add(newEmptyNode())
+  else:
+    var base_type = newNimNode(nnkOfInherit)
+    base_type.add(ident(typedef.base_type))
+    object_node.add(base_type)
+  let fields = typedef.fields.render()
+  object_node.add(fields)
+  if typedef.is_ref:
+    let ref_node = newNimNode(nnkRefTy)
+    ref_node.add(object_node)
+    result.add(ref_node)
+  else:
+    result.add(object_node)
+
+proc newTypeDefSeq*(node: NimNode): TypeDefSeq =
+  result = @[]
+  case node.kind:
+    of nnkTypeSection:
+      for child in node:
+        result.add(newTypeDef(child))
+    else:
+      let msg = "newTypeSection could not parse TypeDef, found: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+proc render*(typeseq: TypeDefSeq): NimNode {.compileTime.} =
+  result = newNimNode(nnkTypeSection)
+  for typedef in typeseq:
+    result.add(typedef.render())
+
+proc newProc*(identifier: Ident, params: FieldSeq = nil,
+              returns: Ident, generics: FieldSeq = nil): Proc =
+  result.identifier = identifier
+  result.params = params
+  result.returns = returns
+  result.generics = generics
+
+proc newProc*(node: NimNode): Proc =
+  case node.kind:
+    of nnkProcDef, nnkMethodDef:
+      result.identifier = newIdent(node[0])
+      case node[2].kind:
+        of nnkGenericParams:
+          result.generics = newFieldSeq(node[2])
+        else: result.generics = nil
+      let formal_params = node[3]
+      case formal_params[0].kind:
+        of nnkIdent:
+          result.returns = newIdent(formal_params[0])
+        else: discard
+      result.params = @[]
+      for i in 1..formal_params.len - 1:
+        let param = formal_params[i]
+        for field in newFieldSeq(param):
+          result.params.add(field)
+      result.body = node[6]
+    else:
+      let msg = "newProc cannot initialize from node kind: " & $(node.kind)
+      raise newException(ValueError, msg)
+
+proc render*(procdef: Proc): NimNode {.compileTime.} =
+  result = newNimNode(nnkProcDef)
+  result.add(procdef.identifier.render())
+  result.add(newEmptyNode())
+  result.add(newEmptyNode())
+  let formal_params = newNimNode(nnkFormalParams)
+  formal_params.add(procdef.returns.render())
+  for param in procdef.params:
+    formal_params.add(param.render())
+  result.add(formal_params)
+  result.add(newEmptyNode())
+  result.add(newEmptyNode())
+  result.add(procdef.body)
diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim
new file mode 100644
index 000000000..efeba2a6d
--- /dev/null
+++ b/tests/vm/tcomponent.nim
@@ -0,0 +1,132 @@
+discard """
+  output: '''`:)` @ 0,0
+FOO: blah'''
+"""
+
+#
+# magic.nim
+#
+
+# bug #3729
+
+import macros, sequtils, tables
+import strutils
+import future, meta
+
+type
+  Component = object
+    fields: FieldSeq
+    field_index: seq[string]
+    procs: ProcSeq
+    procs_index: seq[string]
+
+  Registry = object
+    field_index: seq[string]
+    procs_index: seq[string]
+    components: Table[string, Component]
+    builtin: Component
+
+proc newRegistry(): Registry =
+  result.field_index = @[]
+  result.procs_index = @[]
+  result.components = initTable[string, Component]()
+
+var registry {.compileTime.} = newRegistry()
+
+proc validateComponent(r: var Registry, name: string, c: Component) =
+  if r.components.hasKey(name):
+    let msg = "`component` macro cannot consume duplicated identifier: " & name
+    raise newException(ValueError, msg)
+
+  for field_name in c.field_index:
+    if r.field_index.contains(field_name):
+      let msg = "`component` macro cannot delcare duplicated field: " & field_name
+      raise newException(ValueError, msg)
+    r.field_index.add(field_name)
+
+  for proc_name in c.procs_index:
+    if r.procs_index.contains(proc_name):
+      let msg = "`component` macro cannot delcare duplicated proc: " & proc_name
+      raise newException(ValueError, msg)
+    r.procs_index.add(proc_name)
+
+proc addComponent(r: var Registry, name: string, c: Component) =
+  r.validateComponent(name, c)
+  r.components.add(name, c)
+
+proc parse_component(body: NimNode): Component =
+  result.field_index = @[]
+  result.procs_index = @[]
+  for node in body:
+    case node.kind:
+      of nnkVarSection:
+        result.fields = newFieldSeq(node)
+        for field in result.fields:
+          result.field_index.add(field.identifier.name)
+      of nnkMethodDef, nnkProcDef:
+        let new_proc = meta.newProc(node)
+        result.procs = result.procs & @[new_proc]
+        for procdef in result.procs:
+          result.procs_index.add(procdef.identifier.name)
+      else: discard
+
+macro component*(name: expr, body: stmt): stmt {.immediate.} =
+  let component = parse_component(body)
+  registry.addComponent($name, component)
+  parseStmt("discard")
+
+macro component_builtins(body: stmt): stmt {.immediate.} =
+  let builtin = parse_component(body)
+  registry.field_index = builtin.field_index
+  registry.procs_index = builtin.procs_index
+  registry.builtin = builtin
+
+proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] =
+  result = @[]
+  for procdef in component.procs.mitems:
+    let this_field = newField(newIdent("this"), identifier)
+    procdef.params.insert(this_field, 0)
+    result.add(procdef.render())
+
+macro bind_components*(type_name, component_names: expr): stmt {.immediate.} =
+  result = newStmtList()
+  let identifier = newIdent(type_name)
+  let components = newBracket(component_names)
+  var entity_type = newTypeDef(identifier, true, "object", "RootObj")
+  entity_type.fields = registry.builtin.fields
+  for component_name, component in registry.components:
+    if components.contains(newIdent(component_name)):
+      entity_type.fields = entity_type.fields & component.fields
+  # TODO why doesn't the following snippet work instead of the one above?
+  # for name in components:
+  #   echo "Registering $1 to $2" % [name.name, identifier.name]
+  #   let component = registry.components[name.name]
+  #   entity_type.fields = entity_type.fields & component.fields
+  let type_section: TypeDefSeq = @[entity_type]
+  result.add type_section.render
+  var builtin = registry.builtin
+  let builtin_methods = bind_methods(builtin, identifier)
+  for builtin_proc in builtin_methods:
+    result.add(builtin_proc)
+  echo "SIGSEV here"
+  for component in registry.components.mvalues():
+    for method_proc in bind_methods(component, identifier):
+      result.add(method_proc)
+
+component_builtins:
+  proc foo(msg: string) =
+    echo "FOO: $1" % msg
+
+component position:
+  var x*, y*: int
+
+component name:
+  var name*: string
+  proc render*(x, y: int) = echo "`$1` @ $2,$3" % [this.name, $x, $y]
+
+bind_components(Entity, [position, name])
+
+var e = new(Entity)
+e.name = ":)"
+e.render(e.x, e.y)
+e.foo("blah")
diff --git a/todo.txt b/todo.txt
index a6312468a..542f096fb 100644
--- a/todo.txt
+++ b/todo.txt
@@ -2,7 +2,7 @@
 nim c --gc:v2 -r -d:useSysAssert -d:useGcAssert -d:smokeCycles -d:useRealtimeGc tests/gc/gctest
 
 - document ``this`` pragma
-- document and stress test ``.partial`` object declarations
+- https://github.com/nim-lang/Nim/issues/3898
 
 essential for 1.0
 =================
@@ -24,6 +24,7 @@ essential for 1.0
 Not critical for 1.0
 ====================
 
+- document and stress test ``.partial`` object declarations
 - add "all threads are blocked" detection to 'spawn'
 - figure out why C++ bootstrapping is so much slower
 - The bitwise 'not' operator cold be renamed to 'bnot' to
@@ -62,7 +63,6 @@ Bugs
 GC
 ==
 
-- hybrid GC
 - use big blocks in the allocator
 - provide tool/API to track leaks/object counts
 - resizing of strings/sequences could take into account the memory that
diff --git a/web/news.txt b/web/news.txt
index b6ce533c8..12f08e5eb 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -2,7 +2,7 @@
 News
 ====
 
-2016-XX-XX Version 0.13.1 released
+2016-XX-XX Version 0.14.0 released
 ==================================
 
 Changes affecting backwards compatibility
@@ -37,6 +37,24 @@ Changes affecting backwards compatibility
   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 ``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.
 
 
 Library Additions
@@ -48,6 +66,12 @@ Library Additions
 - 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
@@ -62,11 +86,13 @@ Language Additions
 
 - Nim now supports a ``.this`` pragma for more notational convenience.
 - Nim now supports a different ``using`` statement for more convenience.
-- 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.
 - ``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.
+
 
 2016-01-27 Nim in Action is now available!
 ==========================================
diff --git a/web/website.ini b/web/website.ini
index 5b3382820..58207a1c0 100644
--- a/web/website.ini
+++ b/web/website.ini
@@ -39,7 +39,7 @@ srcdoc2: "impure/re;impure/nre;pure/typetraits"
 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"
+srcdoc2: "pure/stats;impure/nre;windows/winlean;pure/random"
 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"