summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-06-26 18:33:51 +0200
committerGitHub <noreply@github.com>2018-06-26 18:33:51 +0200
commit2a3a128e362929bac9c2dbf7430cbe8732840f95 (patch)
treeac794774866b8b71c5a6e271a21b42b060b57900
parente129466910b6efa730f8d5d9232efbce6dae46f0 (diff)
parentd08b9eb6731a70504be6d856723fbc94dc7bd506 (diff)
downloadNim-2a3a128e362929bac9c2dbf7430cbe8732840f95.tar.gz
Merge branch 'devel' into typedesc-reforms
-rw-r--r--.travis.yml2
-rw-r--r--appveyor.yml2
-rw-r--r--changelog.md3
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/ccgstmts.nim21
-rw-r--r--compiler/commands.nim5
-rw-r--r--compiler/docgen.nim8
-rw-r--r--compiler/extccomp.nim2
-rw-r--r--compiler/layouter.nim164
-rw-r--r--compiler/lexer.nim14
-rw-r--r--compiler/parser.nim28
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semfold.nim13
-rw-r--r--compiler/semstmts.nim11
-rw-r--r--compiler/sighashes.nim7
-rw-r--r--compiler/types.nim2
-rw-r--r--doc/tut1.rst5
-rw-r--r--koch.nim8
-rw-r--r--lib/pure/httpclient.nim2
-rw-r--r--lib/pure/pegs.nim23
-rw-r--r--lib/pure/strutils.nim4
-rw-r--r--lib/pure/terminal.nim131
-rw-r--r--lib/pure/times.nim47
-rw-r--r--lib/std/varints.nim9
-rw-r--r--nimpretty/nimpretty.nim (renamed from tools/nimpretty.nim)13
-rw-r--r--nimpretty/nimpretty.nim.cfg (renamed from tools/nimpretty.nim.cfg)0
-rw-r--r--nimpretty/tester.nim29
-rw-r--r--nimpretty/tests/exhaustive.nim316
-rw-r--r--nimpretty/tests/expected/exhaustive.nim325
-rw-r--r--tests/arithm/tnot.nim58
-rw-r--r--tests/arithm/tshl.nim34
-rw-r--r--tests/concepts/treversable.nim31
-rw-r--r--tests/niminaction/Chapter1/various1.nim45
-rw-r--r--tests/niminaction/Chapter2/explicit_discard.nim7
-rw-r--r--tests/niminaction/Chapter2/no_def_eq.nim16
-rw-r--r--tests/niminaction/Chapter2/no_iterator.nim7
-rw-r--r--tests/niminaction/Chapter2/no_seq_type.nim6
-rw-r--r--tests/niminaction/Chapter2/resultaccept.nim28
-rw-r--r--tests/niminaction/Chapter2/resultreject.nim33
-rw-r--r--tests/niminaction/Chapter2/various2.nim369
-rw-r--r--tests/niminaction/Chapter3/various3.nim93
-rw-r--r--tests/niminaction/Chapter3/various3.nim.cfg1
-rw-r--r--tests/stdlib/tpegs.nim78
-rw-r--r--tests/testament/categories.nim38
-rw-r--r--tests/testament/tester.nim2
-rw-r--r--tests/types/t7905.nim33
47 files changed, 1918 insertions, 165 deletions
diff --git a/.travis.yml b/.travis.yml
index b7880cd36..5a091d0c7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -45,6 +45,8 @@ script:
   - nimble install niminst
   - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
   - tests/testament/tester --pedantic all -d:nimCoroutines
+  - nim c -o:bin/nimpretty nimpretty/nimpretty.nim
+  - nim c -r nimpretty/tester.nim
   - ./koch web
   - ./koch csource
   - ./koch nimsuggest
diff --git a/appveyor.yml b/appveyor.yml
index a79d32e41..daa1d4e48 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -47,6 +47,8 @@ build_script:
   - koch boot -d:release
   - koch nimble
   - nim e tests/test_nimscript.nims
+  - nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim
+  - nim c -r nimpretty/tester.nim
   - nimble install zip -y
   - nimble install opengl
   - nimble install sdl1
diff --git a/changelog.md b/changelog.md
index 2e75d8ad9..31b31c5bf 100644
--- a/changelog.md
+++ b/changelog.md
@@ -107,6 +107,9 @@
   use the Nim VM in a native Nim application.
 - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
 - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
+- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
+  object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
+  iterators.
 
 ### Language additions
 
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 82cc3a1fb..0e8af5af5 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -59,7 +59,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     else:
       result = rope("NIM_NIL")
   of nkStrLit..nkTripleStrLit:
-    case skipTypes(ty, abstractVarRange).kind
+    case skipTypes(ty, abstractVarRange + {tyStatic}).kind
     of tyNil:
       result = genNilStringLiteral(p.module, n.info)
     of tyString:
@@ -385,7 +385,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
     else:
       addrLoc(p.config, a)
 
-  var ty = skipTypes(dest.t, abstractVarRange)
+  var ty = skipTypes(dest.t, abstractVarRange + {tyStatic})
   case ty.kind
   of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
     # XXX optimize this
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 71d212282..f9654bb1f 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -978,16 +978,17 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
   if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
     for x in splitLines(res):
       var j = 0
-      while x[j] in {' ', '\t'}: inc(j)
-      if x[j] in {'"', ':'}:
-        # don't modify the line if already in quotes or
-        # some clobber register list:
-        add(result, x); add(result, "\L")
-      elif x[j] != '\0':
-        # ignore empty lines
-        add(result, "\"")
-        add(result, x)
-        add(result, "\\n\"\n")
+      while j < x.len and x[j] in {' ', '\t'}: inc(j)
+      if j < x.len:
+        if x[j] in {'"', ':'}:
+          # don't modify the line if already in quotes or
+          # some clobber register list:
+          add(result, x); add(result, "\L")
+        else:
+          # ignore empty lines
+          add(result, "\"")
+          add(result, x)
+          add(result, "\\n\"\n")
   else:
     res.add("\L")
     result = res.rope
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 330504a76..866405f9f 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -606,7 +606,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     incl(conf.globalOptions, optRun)
   of "verbosity":
     expectArg(conf, switch, arg, pass, info)
-    conf.verbosity = parseInt(arg)
+    let verbosity = parseInt(arg)
+    if verbosity notin {0..3}:
+      localError(conf, info, "invalid verbosity level: '$1'" % arg)
+    conf.verbosity = verbosity
     conf.notes = NotesVerbosity[conf.verbosity]
     incl(conf.notes, conf.enableNotes)
     excl(conf.notes, conf.disableNotes)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index d463dc3c0..db4e301d4 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -582,7 +582,7 @@ proc traceDeps(d: PDoc, it: PNode) =
     if d.section[k] != nil: add(d.section[k], ", ")
     dispA(d.conf, d.section[k],
           "<a class=\"reference external\" href=\"$1.html\">$1</a>",
-          "$1", [rope(getModuleName(d.conf, it))])
+          "$1", [rope(splitFile(getModuleName(d.conf, it)).name)])
 
 proc generateDoc*(d: PDoc, n: PNode) =
   case n.kind
@@ -780,13 +780,11 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
 proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
   var success = true
-  var filename: string
   if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
-    filename = "<stdout>"
   else:
-    filename = getOutFile2(d.conf, filename, outExt, "htmldocs")
-    success = writeRope(content, filename)
+    let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs")
+    success = writeRope(content, outfile)
   if not success:
     rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename)
 
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 615b8c1e1..17133624b 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -603,7 +603,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
 
 proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
   var c = Cfile(cname: filename,
-    obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
+    obj: toObjFile(conf, completeCFilePath(conf, filename, false)),
     flags: {CfileFlag.External})
   addExternalFileToCompile(conf, c)
 
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index 90e9d6fd7..36ad08696 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -7,11 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Layouter for nimpretty. Still primitive but useful.
-## TODO
-## - Fix 'echo ()' vs 'echo()' difference!
-## - Make indentations consistent.
-## - Align 'if' and 'case' expressions properly.
+## Layouter for nimpretty.
 
 import idents, lexer, lineinfos, llstream, options, msgs, strutils
 from os import changeFileExt
@@ -24,32 +20,44 @@ type
   SplitKind = enum
     splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
 
+  SemicolonKind = enum
+    detectSemicolonKind, useSemicolon, dontTouch
+
   Emitter* = object
-    f: PLLStream
     config: ConfigRef
     fid: FileIndex
     lastTok: TTokType
     inquote: bool
-    col, lastLineNumber, lineSpan, indentLevel: int
+    semicolons: SemicolonKind
+    col, lastLineNumber, lineSpan, indentLevel, indWidth: int
+    nested: int
+    doIndentMore*: int
     content: string
+    indentStack: seq[int]
     fixedUntil: int # marks where we must not go in the content
     altSplitPos: array[SplitKind, int] # alternative split positions
 
-proc openEmitter*(em: var Emitter, config: ConfigRef, fileIdx: FileIndex) =
-  let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
-  em.f = llStreamOpen(outfile, fmWrite)
+proc openEmitter*(em: var Emitter, cache: IdentCache;
+                  config: ConfigRef, fileIdx: FileIndex) =
+  let fullPath = config.toFullPath(fileIdx)
+  em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
+                               cache, config)
+  if em.indWidth == 0: em.indWidth = 2
   em.config = config
   em.fid = fileIdx
   em.lastTok = tkInvalid
   em.inquote = false
   em.col = 0
   em.content = newStringOfCap(16_000)
-  if em.f == nil:
-    rawMessage(config, errGenerated, "cannot open file: " & outfile)
+  em.indentStack = newSeqOfCap[int](30)
+  em.indentStack.add 0
 
 proc closeEmitter*(em: var Emitter) =
-  em.f.llStreamWrite em.content
-  llStreamClose(em.f)
+  var f = llStreamOpen(em.config.outFile, fmWrite)
+  if f == nil:
+    rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile)
+  f.llStreamWrite em.content
+  llStreamClose(f)
 
 proc countNewlines(s: string): int =
   result = 0
@@ -69,28 +77,41 @@ template wr(x) =
 
 template goodCol(col): bool = col in 40..MaxLineLen
 
-const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
-                   tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
-                   tkCurlyLe}
+const
+  openPars = {tkParLe, tkParDotLe,
+              tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+              tkCurlyLe}
+  splitters = openPars + {tkComma, tkSemicolon}
+  oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
+            tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
 
 template rememberSplit(kind) =
   if goodCol(em.col):
     em.altSplitPos[kind] = em.content.len
 
+template moreIndent(em): int =
+  (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth)
+
 proc softLinebreak(em: var Emitter, lit: string) =
   # XXX Use an algorithm that is outlined here:
   # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
   # +2 because we blindly assume a comma or ' &' might follow
   if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
     if em.lastTok in splitters:
+      while em.content.len > 0 and em.content[em.content.high] == ' ':
+        setLen(em.content, em.content.len-1)
       wr("\L")
       em.col = 0
-      for i in 1..em.indentLevel+2: wr(" ")
+      for i in 1..em.indentLevel+moreIndent(em): wr(" ")
     else:
       # search backwards for a good split position:
       for a in em.altSplitPos:
         if a > em.fixedUntil:
-          let ws = "\L" & repeat(' ',em.indentLevel+2)
+          var spaces = 0
+          while a+spaces < em.content.len and em.content[a+spaces] == ' ':
+            inc spaces
+          if spaces > 0: delete(em.content, a, a+spaces-1)
+          let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em))
           em.col = em.content.len - a
           em.content.insert(ws, a)
           break
@@ -98,7 +119,7 @@ proc softLinebreak(em: var Emitter, lit: string) =
 proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
 
   template endsInWhite(em): bool =
-    em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
+    em.content.len == 0 or em.content[em.content.high] in {' ', '\L'}
   template endsInAlpha(em): bool =
     em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
 
@@ -120,14 +141,32 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     em.fixedUntil = em.content.high
 
   elif tok.indent >= 0:
-    em.indentLevel = tok.indent
+    if em.lastTok in (splitters + oprSet):
+      em.indentLevel = tok.indent
+    else:
+      if tok.indent > em.indentStack[^1]:
+        em.indentStack.add tok.indent
+      else:
+        # dedent?
+        while em.indentStack.len > 1 and em.indentStack[^1] > tok.indent:
+          discard em.indentStack.pop()
+      em.indentLevel = em.indentStack.high * em.indWidth
+    #[ we only correct the indentation if it is not in an expression context,
+       so that code like
+
+        const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                          tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                          tkCurlyLe}
+
+       is not touched.
+    ]#
     # remove trailing whitespace:
     while em.content.len > 0 and em.content[em.content.high] == ' ':
       setLen(em.content, em.content.len-1)
     wr("\L")
     for i in 2..tok.line - em.lastLineNumber: wr("\L")
     em.col = 0
-    for i in 1..tok.indent:
+    for i in 1..em.indentLevel:
       wr(" ")
     em.fixedUntil = em.content.high
 
@@ -135,49 +174,64 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
   of tokKeywordLow..tokKeywordHigh:
     if endsInAlpha(em):
       wr(" ")
-    elif not em.inquote and not endsInWhite(em):
+    elif not em.inquote and not endsInWhite(em) and
+        em.lastTok notin openPars:
+      #and tok.tokType in oprSet
       wr(" ")
 
-    wr(TokTypeToStr[tok.tokType])
+    if not em.inquote:
+      wr(TokTypeToStr[tok.tokType])
 
-    case tok.tokType
-    of tkAnd: rememberSplit(splitAnd)
-    of tkOr: rememberSplit(splitOr)
-    of tkIn, tkNotin:
-      rememberSplit(splitIn)
-      wr(" ")
-    else: discard
+      case tok.tokType
+      of tkAnd: rememberSplit(splitAnd)
+      of tkOr: rememberSplit(splitOr)
+      of tkIn, tkNotin:
+        rememberSplit(splitIn)
+        wr(" ")
+      else: discard
+    else:
+      # keywords in backticks are not normalized:
+      wr(tok.ident.s)
 
   of tkColon:
     wr(TokTypeToStr[tok.tokType])
     wr(" ")
   of tkSemicolon, tkComma:
     wr(TokTypeToStr[tok.tokType])
-    wr(" ")
     rememberSplit(splitComma)
-  of tkParLe, tkParRi, tkBracketLe,
-     tkBracketRi, tkCurlyLe, tkCurlyRi,
-     tkBracketDotLe, tkBracketDotRi,
-     tkCurlyDotLe, tkCurlyDotRi,
-     tkParDotLe, tkParDotRi,
-     tkColonColon, tkDot, tkBracketLeColon:
+    wr(" ")
+  of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe,
+     tkCurlyLe, tkCurlyDotLe, tkBracketLeColon:
+    if tok.strongSpaceA > 0 and not em.endsInWhite:
+      wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    rememberSplit(splitParLe)
+  of tkParRi,
+     tkBracketRi, tkCurlyRi,
+     tkBracketDotRi,
+     tkCurlyDotRi,
+     tkParDotRi,
+     tkColonColon, tkDot:
     wr(TokTypeToStr[tok.tokType])
-    if tok.tokType in splitters:
-      rememberSplit(splitParLe)
   of tkEquals:
-    if not em.endsInWhite: wr(" ")
+    if not em.inquote and not em.endsInWhite: wr(" ")
     wr(TokTypeToStr[tok.tokType])
-    wr(" ")
+    if not em.inquote: wr(" ")
   of tkOpr, tkDotDot:
-    if not em.endsInWhite: wr(" ")
-    wr(tok.ident.s)
-    template isUnary(tok): bool =
-      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+    if tok.strongSpaceA == 0 and tok.strongSpaceB == 0:
+      # if not surrounded by whitespace, don't produce any whitespace either:
+      wr(tok.ident.s)
+    else:
+      if not em.endsInWhite: wr(" ")
+      wr(tok.ident.s)
+      template isUnary(tok): bool =
+        tok.strongSpaceB == 0 and tok.strongSpaceA > 0
 
-    if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
-      wr(" ")
-      rememberSplit(splitBinary)
+      if not isUnary(tok):
+        wr(" ")
+        rememberSplit(splitBinary)
   of tkAccent:
+    if not em.inquote and endsInAlpha(em): wr(" ")
     wr(TokTypeToStr[tok.tokType])
     em.inquote = not em.inquote
   of tkComment:
@@ -206,3 +260,15 @@ proc starWasExportMarker*(em: var Emitter) =
     setLen(em.content, em.content.len-3)
     em.content.add("*")
     dec em.col, 2
+
+proc commaWasSemicolon*(em: var Emitter) =
+  if em.semicolons == detectSemicolonKind:
+    em.semicolons = if em.content.endsWith(", "): dontTouch else: useSemicolon
+  if em.semicolons == useSemicolon and em.content.endsWith(", "):
+    setLen(em.content, em.content.len-2)
+    em.content.add("; ")
+
+proc curlyRiWasPragma*(em: var Emitter) =
+  if em.content.endsWith("}"):
+    setLen(em.content, em.content.len-1)
+    em.content.add(".}")
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 13460f7c1..c5afa6e97 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -867,7 +867,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
   if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
     tok.strongSpaceB = -1
 
-proc newlineFollows*(L: var TLexer): bool =
+proc newlineFollows*(L: TLexer): bool =
   var pos = L.bufpos
   var buf = L.buf
   while true:
@@ -1220,3 +1220,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
         inc(L.bufpos)
   atTokenEnd()
+
+proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
+                     cache: IdentCache; config: ConfigRef): int =
+  var lex: TLexer
+  var tok: TToken
+  initToken(tok)
+  openLexer(lex, fileIdx, inputstream, cache, config)
+  while true:
+    rawGetTok(lex, tok)
+    result = tok.indent
+    if result > 0 or tok.tokType == tkEof: break
+  closeLexer(lex)
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 5c99363c9..c513fac68 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -108,7 +108,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
   initToken(p.tok)
   openLexer(p.lex, fileIdx, inputStream, cache, config)
   when defined(nimpretty2):
-    openEmitter(p.em, config, fileIdx)
+    openEmitter(p.em, cache, config, fileIdx)
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
@@ -416,6 +416,8 @@ proc exprColonEqExpr(p: var TParser): PNode =
 
 proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
   #| exprList = expr ^+ comma
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   getTok(p)
   optInd(p, result)
   # progress guaranteed
@@ -425,6 +427,8 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
   assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
@@ -856,7 +860,11 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
   result = parseOperators(p, result, limit, mode)
 
 proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   result = simpleExprAux(p, -1, mode)
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
   #| condExpr = expr colcom expr optInd
@@ -931,8 +939,12 @@ proc parsePragma(p: var TParser): PNode =
       getTok(p)
       skipComment(p, a)
   optPar(p)
-  if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
-  else: parMessage(p, "expected '.}'")
+  if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}:
+    when defined(nimpretty2):
+      if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em)
+    getTok(p)
+  else:
+    parMessage(p, "expected '.}'")
   dec p.inPragma
 
 proc identVis(p: var TParser; allowDot=false): PNode =
@@ -1019,6 +1031,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
       var a = parseIdentColonEquals(p, {})
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
+      when defined(nimpretty2):
+        commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
     optPar(p)
@@ -1053,6 +1067,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   var a: PNode
   result = newNodeP(nkFormalParams, p)
   addSon(result, p.emptyNode) # return type
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
   if hasParLe:
     getTok(p)
@@ -1072,6 +1088,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
         break
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
+      when defined(nimpretty2):
+        commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
     optPar(p)
@@ -1085,6 +1103,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   elif not retColon and not hasParle:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
     result = p.emptyNode
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc optPragmas(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
@@ -1678,6 +1698,8 @@ proc parseGenericParamList(p: var TParser): PNode =
     var a = parseGenericParam(p)
     addSon(result, a)
     if p.tok.tokType notin {tkComma, tkSemiColon}: break
+    when defined(nimpretty2):
+      commaWasSemicolon(p.em)
     getTok(p)
     skipComment(p, a)
   optPar(p)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index c78a3519c..afe60e9dd 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -466,7 +466,7 @@ proc processCompile(c: PContext, n: PNode) =
       else:
         found = findFile(c.config, s)
         if found.len == 0: found = s
-    let obj = toObjFile(c.config, completeCFilePath(c.config, changeFileExt(found, ""), false))
+    let obj = toObjFile(c.config, completeCFilePath(c.config, found, false))
     docompile(c, it, found, obj)
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index a9258fb62..5d6eaf652 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -289,7 +289,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
     localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m])
   else:
     n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
+    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst})
     case typ.kind
     of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
       n.typ = getSysType(c.graph, n.info, tyInt)
@@ -1351,7 +1351,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # make sure we don't evaluate generic macros/templates
   n.sons[0] = semExprWithType(c, n.sons[0],
                               {efNoEvaluateGeneric})
-  var arr = skipTypes(n.sons[0].typ, {tyGenericInst,
+  var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
   if arr.kind == tyStatic:
     if arr.base.kind == tyNone:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 10a223ea2..2f495bc7f 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -211,7 +211,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g)
   of mNot: result = newIntNodeT(1 - getInt(a), n, g)
   of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g)
-  of mBitnotI: result = newIntNodeT(not getInt(a), n, g)
+  of mBitnotI:
+    case skipTypes(n.typ, abstractRange).kind
+    of tyUInt..tyUInt64:
+      result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
+    else:
+      result = newIntNodeT(not getInt(a), n, g)
   of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
   of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
     if a.kind == nkNilLit:
@@ -250,8 +255,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
     of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g)
     of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g)
     of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g)
-    of tyInt64, tyInt, tyUInt..tyUInt64:
+    of tyInt64, tyInt:
       result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g)
+    of tyUInt..tyUInt64:
+      result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
     else: internalError(g.config, n.info, "constant folding for shl")
   of mShrI:
     case skipTypes(n.typ, abstractRange).kind
@@ -612,7 +619,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       of mLow:
         result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g)
       of mHigh:
-        if skipTypes(n.sons[1].typ, abstractVar).kind notin
+        if skipTypes(n.sons[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin
             {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
           result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
         else:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 292238dc9..f1ff38b01 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -519,7 +519,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         localError(c.config, a.info, errWrongNumberOfVariables)
       b = newNodeI(nkVarTuple, a.info)
       newSons(b, length)
-      b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
+      # keep type desc for doc generator
+      # NOTE: at the moment this is always ast.emptyNode, see parser.nim
+      b.sons[length-2] = a.sons[length-2]
       b.sons[length-1] = def
       addToVarSection(c, result, n, b)
     elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
@@ -558,7 +560,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           # keep documentation information:
           b.comment = a.comment
         addSon(b, newSymNode(v))
-        addSon(b, a.sons[length-2])      # keep type desc for doc generator
+        # keep type desc for doc generator, but only if the user explicitly
+        # added it
+        if a.sons[length-2].kind != nkEmpty:
+          addSon(b, newNodeIT(nkType, a.info, typ))
+        else:
+          addSon(b, a.sons[length-2])
         addSon(b, copyTree(def))
         addToVarSection(c, result, n, b)
       else:
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 0b95387cd..0bf2b8459 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -189,18 +189,19 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         c.hashTypeSym(t.sym)
       else:
         c.hashSym(t.sym)
-      if sfAnon in t.sym.flags:
+      if {sfAnon, sfGenSym} * t.sym.flags != {}:
         # generated object names can be identical, so we need to
         # disambiguate furthermore by hashing the field types and names:
         # mild hack to prevent endless recursions (makes nimforum compile again):
-        excl t.sym.flags, sfAnon
+        let oldFlags = t.sym.flags
+        t.sym.flags = t.sym.flags - {sfAnon, sfGenSym}
         let n = t.n
         for i in 0 ..< n.len:
           assert n[i].kind == nkSym
           let s = n[i].sym
           c.hashSym s
           c.hashType s.typ, flags
-        incl t.sym.flags, sfAnon
+        t.sym.flags = oldFlags
     else:
       c &= t.id
     if t.len > 0 and t.sons[0] != nil:
diff --git a/compiler/types.nim b/compiler/types.nim
index 7f4f86fae..f78e975ee 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -618,7 +618,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred, tyUserTypeClassInst:
     result = firstOrd(conf, lastSon(t))
   of tyOrdinal:
     if t.len > 0: result = firstOrd(conf, lastSon(t))
diff --git a/doc/tut1.rst b/doc/tut1.rst
index f2251c463..aa6114cf7 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -208,7 +208,8 @@ Note that declaring multiple variables with a single assignment which calls a
 procedure can have unexpected results: the compiler will *unroll* the
 assignments and end up calling the procedure several times. If the result of
 the procedure depends on side effects, your variables may end up having
-different values! For safety use only constant values.
+different values! For safety use side-effect free procedures if making multiple
+assignments.
 
 
 Constants
@@ -642,7 +643,7 @@ initialisation.
 
 Parameters
 ----------
-Parameters are constant in the procedure body. By default, their value cannot be
+Parameters are immutable in the procedure body. By default, their value cannot be
 changed because this allows the compiler to implement parameter passing in the
 most efficient way. If a mutable variable is needed inside the procedure, it has
 to be declared with ``var`` in the procedure body. Shadowing the parameter name
diff --git a/koch.nim b/koch.nim
index 4f85c6583..97e1da776 100644
--- a/koch.nim
+++ b/koch.nim
@@ -254,15 +254,13 @@ proc buildTool(toolname, args: string) =
   copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
 
 proc buildTools(latest: bool) =
-  let nimsugExe = "bin/nimsuggest".exe
-  nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe &
+  nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) &
       " nimsuggest/nimsuggest.nim"
 
-  let nimgrepExe = "bin/nimgrep".exe
-  nimexec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim"
+  nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim"
   when defined(windows): buildVccTool()
 
-  #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
+  nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim"
 
   buildNimble(latest)
 
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 8530e4c42..8b4fb0f8c 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -1184,7 +1184,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   ## Connects to the hostname specified by the URL and performs a request
   ## using the custom method string specified by ``httpMethod``.
   ##
-  ## Connection will kept alive. Further requests on the same ``client`` to
+  ## Connection will be kept alive. Further requests on the same ``client`` to
   ## the same hostname will not require a new connection to be made. The
   ## connection can be closed by using the ``close`` procedure.
   ##
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 39c5790ed..d16527a56 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -32,7 +32,7 @@ const
                        ## can be captured. More subpatterns cannot be captured!
 
 type
-  PegKind = enum
+  PegKind* = enum
     pkEmpty,
     pkAny,              ## any character (.)
     pkAnyRune,          ## any Unicode character (_)
@@ -67,7 +67,7 @@ type
     pkRule,             ## a <- b
     pkList,             ## a, b
     pkStartAnchor       ## ^      --> Internal DSL: startAnchor()
-  NonTerminalFlag = enum
+  NonTerminalFlag* = enum
     ntDeclared, ntUsed
   NonTerminalObj = object         ## represents a non terminal symbol
     name: string                  ## the name of the symbol
@@ -86,6 +86,25 @@ type
     else: sons: seq[Peg]
   NonTerminal* = ref NonTerminalObj
 
+proc name*(nt: NonTerminal): string = nt.name
+proc line*(nt: NonTerminal): int = nt.line
+proc col*(nt: NonTerminal): int = nt.col
+proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags
+proc rule*(nt: NonTerminal): Peg = nt.rule
+
+proc kind*(p: Peg): PegKind = p.kind
+proc term*(p: Peg): string = p.term
+proc ch*(p: Peg): char = p.ch
+proc charChoice*(p: Peg): ref set[char] = p.charChoice
+proc nt*(p: Peg): NonTerminal = p.nt
+proc index*(p: Peg): range[0..MaxSubpatterns] = p.index
+iterator items*(p: Peg): Peg {.inline.} =
+  for s in p.sons:
+    yield s
+iterator pairs*(p: Peg): (int, Peg) {.inline.} =
+  for i in 0 ..< p.sons.len:
+    yield (i, p.sons[i])
+
 proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
   ## constructs a PEG from a terminal string
   if t.len != 1:
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 5de013c26..ab34a0b2d 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -17,6 +17,10 @@ import parseutils
 from math import pow, round, floor, log10
 from algorithm import reverse
 
+when defined(nimVmExportFixed):
+  from unicode import toLower, toUpper
+  export toLower, toUpper
+
 {.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index fcca4d5d7..ac41a0aad 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -467,16 +467,18 @@ proc resetAttributes*(f: File) =
     f.write(ansiResetCode)
 
 type
-  Style* = enum         ## different styles for text output
+  Style* = enum          ## different styles for text output
     styleBright = 1,     ## bright text
     styleDim,            ## dim text
-    styleUnknown,        ## unknown
+    styleItalic,         ## italic (or reverse on terminals not supporting)
     styleUnderscore = 4, ## underscored text
     styleBlink,          ## blinking/bold text
-    styleReverse = 7,    ## unknown
+    styleReverse = 7,    ## reverse
     styleHidden          ## hidden text
+    styleStrikethrough,  ## strikethrough
 
 {.deprecated: [TStyle: Style].}
+{.deprecated: [styleUnknown: styleItalic].}
 
 when not defined(windows):
   var
@@ -540,7 +542,9 @@ type
     fgBlue,                ## blue
     fgMagenta,             ## magenta
     fgCyan,                ## cyan
-    fgWhite                ## white
+    fgWhite,               ## white
+    fg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
+    fgDefault              ## default terminal foreground color
 
   BackgroundColor* = enum  ## terminal's background colors
     bgBlack = 40,          ## black
@@ -550,28 +554,40 @@ type
     bgBlue,                ## blue
     bgMagenta,             ## magenta
     bgCyan,                ## cyan
-    bgWhite                ## white
+    bgWhite,               ## white
+    bg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
+    bgDefault              ## default terminal background color
 
 {.deprecated: [TForegroundColor: ForegroundColor,
                TBackgroundColor: BackgroundColor].}
 
+when defined(windows):
+  var defaultForegroundColor, defaultBackgroundColor: int16 = 0xFFFF'i16 # Default to an invalid value 0xFFFF
+
 proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
   ## Sets the terminal's foreground color.
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not FOREGROUND_RGB
+    if defaultForegroundColor == 0xFFFF'i16:
+      defaultForegroundColor = old
     old = if bright: old or FOREGROUND_INTENSITY
           else:      old and not(FOREGROUND_INTENSITY)
     const lookup: array[ForegroundColor, int] = [
-      0,
+      0, # ForegroundColor enum with ordinal 30
       (FOREGROUND_RED),
       (FOREGROUND_GREEN),
       (FOREGROUND_RED or FOREGROUND_GREEN),
       (FOREGROUND_BLUE),
       (FOREGROUND_RED or FOREGROUND_BLUE),
       (FOREGROUND_BLUE or FOREGROUND_GREEN),
-      (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
-    discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
+      (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED),
+      0, # fg8Bit not supported, see ``enableTrueColors`` instead.
+      0] # unused
+    if fg == fgDefault:
+      discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor))
+    else:
+      discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
@@ -582,18 +598,25 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not BACKGROUND_RGB
+    if defaultBackgroundColor == 0xFFFF'i16:
+      defaultBackgroundColor = old
     old = if bright: old or BACKGROUND_INTENSITY
           else:      old and not(BACKGROUND_INTENSITY)
     const lookup: array[BackgroundColor, int] = [
-      0,
+      0, # BackgroundColor enum with ordinal 40
       (BACKGROUND_RED),
       (BACKGROUND_GREEN),
       (BACKGROUND_RED or BACKGROUND_GREEN),
       (BACKGROUND_BLUE),
       (BACKGROUND_RED or BACKGROUND_BLUE),
       (BACKGROUND_BLUE or BACKGROUND_GREEN),
-      (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
-    discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
+      (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED),
+      0, # bg8Bit not supported, see ``enableTrueColors`` instead.
+      0] # unused
+    if bg == bgDefault:
+      discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor))
+    else:
+      discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
@@ -690,8 +713,8 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) =
   when cmd == bgColor:
     fgSetColor = false
 
-macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
-  ## Similar to ``writeLine``, but treating terminal style arguments specially.
+macro styledWrite*(f: File, m: varargs[typed]): untyped =
+  ## Similar to ``write``, but treating terminal style arguments specially.
   ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``,
   ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to
   ## ``f``, but instead corresponding terminal style proc is called.
@@ -700,8 +723,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
   ##
   ## .. code-block:: nim
   ##
-  ##   proc error(msg: string) =
-  ##     styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
+  ##   stdout.styledWrite(fgRed, "red text ")
+  ##   stdout.styledWrite(fgGreen, "green text")
   ##
   let m = callsite()
   var reset = false
@@ -712,8 +735,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
     case item.kind
     of nnkStrLit..nnkTripleStrLit:
       if i == m.len - 1:
-        # optimize if string literal is last, just call writeLine
-        result.add(newCall(bindSym"writeLine", f, item))
+        # optimize if string literal is last, just call write
+        result.add(newCall(bindSym"write", f, item))
         if reset: result.add(newCall(bindSym"resetAttributes", f))
         return
       else:
@@ -722,16 +745,24 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
     else:
       result.add(newCall(bindSym"styledEchoProcessArg", f, item))
       reset = true
-
-  result.add(newCall(bindSym"write", f, newStrLitNode("\n")))
   if reset: result.add(newCall(bindSym"resetAttributes", f))
 
-macro styledEcho*(args: varargs[untyped]): untyped =
+template styledWriteLine*(f: File, args: varargs[untyped]) =
+  ## Calls ``styledWrite`` and appends a newline at the end.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##   proc error(msg: string) =
+  ##     styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
+  ##
+  styledWrite(f, args)
+  write(f, "\n")
+
+template styledEcho*(args: varargs[untyped]) =
   ## Echoes styles arguments to stdout using ``styledWriteLine``.
-  result = newCall(bindSym"styledWriteLine")
-  result.add(bindSym"stdout")
-  for arg in children(args):
-    result.add(arg)
+  stdout.styledWriteLine(args)
 
 proc getch*(): char =
   ## Read a single character from the terminal, blocking until it is entered.
@@ -779,7 +810,7 @@ when defined(windows):
           inc i, x
         password.string.setLen(max(password.len - x, 0))
       of chr(0x0):
-        # modifier key - ignore - for details see 
+        # modifier key - ignore - for details see
         # https://github.com/nim-lang/Nim/issues/7764
         continue
       else:
@@ -838,16 +869,6 @@ proc resetAttributes*() {.noconv.} =
   ## ``system.addQuitProc(resetAttributes)``.
   resetAttributes(stdout)
 
-when not defined(testing) and isMainModule:
-  #system.addQuitProc(resetAttributes)
-  write(stdout, "never mind")
-  stdout.eraseLine()
-  stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore})
-  stdout.setBackGroundColor(bgCyan, true)
-  stdout.setForeGroundColor(fgBlue)
-  stdout.writeLine("ordinary text")
-  stdout.resetAttributes()
-
 proc isTrueColorSupported*(): bool =
   ## Returns true if a terminal supports true color.
   return trueColorIsSupported
@@ -898,3 +919,43 @@ proc disableTrueColors*() =
       trueColorIsEnabled = false
   else:
     trueColorIsEnabled = false
+
+when not defined(testing) and isMainModule:
+  #system.addQuitProc(resetAttributes)
+  write(stdout, "never mind")
+  stdout.eraseLine()
+  stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ")
+  stdout.styledWriteLine("italic text ", {styleItalic})
+  stdout.setBackGroundColor(bgCyan, true)
+  stdout.setForeGroundColor(fgBlue)
+  stdout.write("blue text in cyan background")
+  stdout.resetAttributes()
+  echo ""
+  stdout.writeLine("ordinary text")
+  echo "more ordinary text"
+  styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!"
+  echo "ordinary text again"
+  styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :("
+  echo "ordinary text again"
+  setForeGroundColor(fgGreen)
+  echo "green text"
+  echo "more green text"
+  setForeGroundColor(fgBlue)
+  echo "blue text"
+  resetAttributes()
+  echo "ordinary text"
+
+  stdout.styledWriteLine(fgRed, "red text ")
+  stdout.styledWriteLine(fgWhite, bgRed, "white text in red background")
+  stdout.styledWriteLine(" ordinary text ")
+  stdout.styledWriteLine(fgGreen, "green text")
+
+  stdout.styledWrite(fgRed, "red text ")
+  stdout.styledWrite(fgWhite, bgRed, "white text in red background")
+  stdout.styledWrite(" ordinary text ")
+  stdout.styledWrite(fgGreen, "green text")
+  echo ""
+  echo "ordinary text"
+  stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text")
+  stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, " bold text in yellow bg", bgDefault, " bold text")
+  echo "ordinary text"
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 7cecc31ab..7fd60b818 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -238,11 +238,11 @@ proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc initTime*(unix: int64, nanosecond: NanosecondRange): Time 
+proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
   {.tags: [], raises: [], benign noSideEffect.}
 
 proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration 
+                   seconds, minutes, hours, days, weeks: int64 = 0): Duration
   {.tags: [], raises: [], benign noSideEffect.}
 
 proc nanosecond*(time: Time): NanosecondRange =
@@ -531,7 +531,7 @@ proc `$`*(dur: Duration): string =
     let quantity = numParts[unit]
     if quantity != 0.int64:
       parts.add(stringifyUnit(quantity, $unit))
-  
+
   result = humanizeParts(parts)
 
 proc `+`*(a, b: Duration): Duration {.operator.} =
@@ -1243,23 +1243,23 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration =
     minutes = interval.minutes,
     hours = interval.hours)
 
-proc between*(startDt, endDt:DateTime): TimeInterval =
+proc between*(startDt, endDt: DateTime): TimeInterval =
   ## Evaluate difference between two dates in ``TimeInterval`` format, so, it
   ## will be relative.
   ##
-  ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in 
-  ## different ``TimeZone's``.  
+  ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in
+  ## different ``TimeZone's``.
   ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC.
   runnableExamples:
-    var a = initDateTime(year = 2018, month = Month(3), monthday = 25, 
+    var a = initDateTime(year = 2018, month = Month(3), monthday = 25,
                      hour = 0, minute = 59, second = 59, nanosecond = 1,
                      zone = utc()).local
-    var b = initDateTime(year = 2018, month = Month(3), monthday = 25, 
+    var b = initDateTime(year = 2018, month = Month(3), monthday = 25,
                      hour = 1, minute =  1, second =  1, nanosecond = 0,
                      zone = utc()).local
     doAssert between(a, b) == initTimeInterval(
       nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1)
-    
+
     a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc())
     b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz")
     doAssert between(a, b) == initTimeInterval(hours=1, days=2)
@@ -1526,7 +1526,6 @@ proc formatToken(dt: DateTime, token: string, buf: var string) =
   else:
     raise newException(ValueError, "Invalid format string: " & token)
 
-
 proc format*(dt: DateTime, f: string): string {.tags: [].}=
   ## This procedure formats `dt` as specified by `f`. The following format
   ## specifiers are available:
@@ -1601,18 +1600,14 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
     inc(i)
   formatToken(dt, currentF, result)
 
-proc format*(time: Time, f: string, zone_info: proc(t: Time): DateTime): string {.tags: [].} =
-  ## converts a `Time` value to a string representation. It will use format from
+proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} =
+  ## Converts a `Time` value to a string representation. It will use format from
   ## ``format(dt: DateTime, f: string)``.
   runnableExamples:
-    var dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
+    var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
     var tm = dt.toTime()
-    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", local) == "1970-01-01T00:00:00"
-    dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
-    tm = dt.toTime()
-    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc) == "1970-01-01T00:00:00"
-
-  zone_info(time).format(f)
+    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00"
+  time.inZone(zone).format(f)
 
 proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
   ## Converts a `DateTime` object to a string representation.
@@ -1984,16 +1979,12 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
 proc toTimeInterval*(time: Time): TimeInterval =
   ## Converts a Time to a TimeInterval.
   ##
-  ## To be used when diffing times.
+  ## To be used when diffing times. Consider using `between` instead.
   runnableExamples:
     let a = fromUnix(10)
-    let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
-    doAssert a.toTimeInterval() == initTimeInterval(
-      years=1970, days=1, seconds=10, hours=convert(
-        Seconds, Hours, -dt.utcOffset
-      )
-    )
-
+    let b = fromUnix(1_500_000_000)
+    let ti = b.toTimeInterval() - a.toTimeInterval()
+    doAssert a + ti == b
   var dt = time.local
   initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour,
     dt.monthday, 0, dt.month.ord - 1, dt.year)
@@ -2150,7 +2141,7 @@ proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
   t.toTimeInterval()
 
 proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
-  ## **Deprecated since v0.18.0:** use 
+  ## **Deprecated since v0.18.0:** use
   ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
   getDayOfWeek(day, month.Month, year)
 
diff --git a/lib/std/varints.nim b/lib/std/varints.nim
index bfc1945fe..483d5c96c 100644
--- a/lib/std/varints.nim
+++ b/lib/std/varints.nim
@@ -19,7 +19,7 @@ proc readVu64*(z: openArray[byte]; pResult: var uint64): int =
     return 1
   if z[0] <= 248:
     if z.len < 2: return 0
-    pResult = (uint64 z[0] - 241) * 256 + uint64 z[1] + 240
+    pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240
     return 2
   if z.len < int(z[0]-246): return 0
   if z[0] == 249:
@@ -135,6 +135,13 @@ when isMainModule:
     if encodeZigzag(decodeZigzag(test)) != test:
       echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test)
 
+  for test in 0u64..300u64:
+    let wrLen = writeVu64(dest, test)
+    let rdLen = readVu64(dest, got)
+    assert wrLen == rdLen
+    if got != test:
+      echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0]
+
   # check this also works for floats:
   for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]:
     let t = cast[uint64](test)
diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim
index 89e6ef905..aa9756c45 100644
--- a/tools/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -25,6 +25,7 @@ Usage:
   nimpretty [options] file.nim
 Options:
   --backup:on|off     create a backup file before overwritting (default: ON)
+  --output:file       set the output file (default: overwrite the .nim file)
   --version           show the version
   --help              show this help
 """
@@ -39,18 +40,18 @@ proc writeVersion() =
   stdout.flushFile()
   quit(0)
 
-proc prettyPrint(infile: string) =
-  let conf = newConfigRef()
+proc prettyPrint(infile, outfile: string) =
+  var conf = newConfigRef()
   let fileIdx = fileInfoIdx(conf, infile)
+  conf.outFile = outfile
   when defined(nimpretty2):
     discard parseFile(fileIdx, newIdentCache(), conf)
   else:
     let tree = parseFile(fileIdx, newIdentCache(), conf)
-    let outfile = changeFileExt(infile, ".pretty.nim")
     renderModule(tree, infile, outfile, {}, fileIdx, conf)
 
 proc main =
-  var infile: string
+  var infile, outfile: string
   var backup = true
   for kind, key, val in getopt():
     case kind
@@ -61,12 +62,14 @@ proc main =
       of "help", "h": writeHelp()
       of "version", "v": writeVersion()
       of "backup": backup = parseBool(val)
+      of "output", "o": outfile = val
       else: writeHelp()
     of cmdEnd: assert(false) # cannot happen
   if infile.len == 0:
     quit "[Error] no input file."
   if backup:
     os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup"))
-  prettyPrint(infile)
+  if outfile.len == 0: outfile = infile
+  prettyPrint(infile, outfile)
 
 main()
diff --git a/tools/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg
index 5fafa6d2a..5fafa6d2a 100644
--- a/tools/nimpretty.nim.cfg
+++ b/nimpretty/nimpretty.nim.cfg
diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim
new file mode 100644
index 000000000..8798ce06a
--- /dev/null
+++ b/nimpretty/tester.nim
@@ -0,0 +1,29 @@
+# Small program that runs the test cases
+
+import strutils, os
+
+const
+  dir = "nimpretty/tests/"
+
+var
+  failures = 0
+
+proc test(infile, outfile: string) =
+  if execShellCmd("nimpretty -o:$2 --backup:off $1" % [infile, outfile]) != 0:
+    quit("FAILURE")
+  let nimFile = splitFile(infile).name
+  let expected = dir / "expected" / nimFile & ".nim"
+  let produced = dir / nimFile & ".pretty"
+  if strip(readFile(expected)) != strip(readFile(produced)):
+    echo "FAILURE: files differ: ", nimFile
+    discard execShellCmd("diff -uNdr " & expected & " " & produced)
+    failures += 1
+  else:
+    echo "SUCCESS: files identical: ", nimFile
+
+for t in walkFiles(dir / "*.nim"):
+  let res = t.changeFileExt("pretty")
+  test(t, res)
+  removeFile(res)
+
+if failures > 0: quit($failures & " failures occurred.")
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
new file mode 100644
index 000000000..a2501a193
--- /dev/null
+++ b/nimpretty/tests/exhaustive.nim
@@ -0,0 +1,316 @@
+discard """
+  outputsub: '''ObjectAssignmentError'''
+  exitcode: "1"
+"""
+
+import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere
+
+proc `[]=`() = discard "index setter"
+proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
+
+(not false)
+
+let expr = if true: "true" else: "false"
+
+var body = newNimNode(nnkIfExpr).add(
+  newNimNode(nnkElifBranch).add(
+    infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))),
+    condition
+  ),
+  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false"))))
+)
+
+# comment
+
+var x = 1
+
+type
+  GeneralTokenizer* = object of RootObj ## comment here
+    kind*: TokenClass ## and here
+    start*, length*: int ## you know how it goes...
+    buf: cstring
+    pos: int # other comment here.
+    state: TokenClass
+
+var x*: string
+var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]#
+
+echo "#", x, "##", y, "#" & "string" & $test
+
+echo (tup, here)
+echo(argA, argB)
+
+import macros
+
+## A documentation comment here.
+## That spans multiple lines.
+## And is not to be touched.
+
+const numbers = [4u8, 5'u16, 89898_00]
+
+macro m(n): untyped =
+  result = foo"string literal"
+
+{.push m.}
+proc p() = echo "p", 1+4 * 5, if true: 5 else: 6
+proc q(param: var ref ptr string) =
+  p()
+  if true:
+    echo a and b or not c and not -d
+{.pop.}
+
+q()
+
+when false:
+  # bug #4766
+  type
+    Plain = ref object
+      discard
+
+    Wrapped[T] = object
+      value: T
+
+  converter toWrapped[T](value: T): Wrapped[T] =
+    Wrapped[T](value: value)
+
+  let result = Plain()
+  discard $result
+
+when false:
+  # bug #3670
+  template someTempl(someConst: bool) =
+    when someConst:
+      var a: int
+    if true:
+      when not someConst:
+        var a: int
+      a = 5
+
+  someTempl(true)
+
+
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Layouter for nimpretty. Still primitive but useful.
+
+import idents, lexer, lineinfos, llstream, options, msgs, strutils
+from os import changeFileExt
+
+const
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+type
+  SplitKind = enum
+    splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
+
+  Emitter* = object
+    f: PLLStream
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote {.pragmaHereWrongCurlyEnd}: bool
+    col, lastLineNumber, lineSpan, indentLevel: int
+    content: string
+    fixedUntil: int # marks where we must not go in the content
+    altSplitPos: array[SplitKind, int] # alternative split positions
+
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
+  let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
+  em.f = llStreamOpen(outfile, fmWrite)
+  em.config = config
+  em.fid = fileIdx
+  em.lastTok = tkInvalid
+  em.inquote = false
+  em.col = 0
+  em.content = newStringOfCap(16_000)
+  if em.f == nil:
+    rawMessage(config, errGenerated, "cannot open file: " & outfile)
+
+proc closeEmitter*(em: var Emitter) {.inline.} =
+  em.f.llStreamWrite em.content
+  llStreamClose(em.f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i+1] == '\L': inc result
+
+proc calcCol(em: var Emitter; s: string) =
+  var i = s.len-1
+  em.col = 0
+  while i >= 0 and s[i] != '\L':
+    dec i
+    inc em.col
+
+template wr(x) =
+  em.content.add x
+  inc em.col, x.len
+
+template goodCol(col): bool = col in 40..MaxLineLen
+
+const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                   tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                   tkCurlyLe}
+
+template rememberSplit(kind) =
+  if goodCol(em.col):
+    em.altSplitPos[kind] = em.content.len
+
+proc softLinebreak(em: var Emitter, lit: string) =
+  # XXX Use an algorithm that is outlined here:
+  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
+  # +2 because we blindly assume a comma or ' &' might follow
+  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
+    if em.lastTok in splitters:
+      wr("\L")
+      em.col = 0
+      for i in 1..em.indentLevel+2: wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          let ws = "\L" & repeat(' ',em.indentLevel+2)
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    if not endsInWhite(em):
+      wr(" ")
+      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
+        for i in 1 .. LineCommentColumn - em.col: wr(" ")
+    wr lit
+
+  var preventComment = case tok.tokType
+                       of tokKeywordLow..tokKeywordHigh:
+                          if endsInAlpha(em): wr(" ")
+                          wr(TokTypeToStr[tok.tokType])
+
+                          case tok.tokType
+                          of tkAnd: rememberSplit(splitAnd)
+                          of tkOr: rememberSplit(splitOr)
+                          of tkIn: rememberSplit(splitIn)
+                          else: 90
+                       else:
+                         "case returns value"
+
+
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
+    # we have an inline comment so handle it before the indentation token:
+    emitComment(em, tok)
+    preventComment = true
+    em.fixedUntil = em.content.high
+
+  elif tok.indent >= 0:
+        em.indentLevel = tok.indent
+        # remove trailing whitespace:
+        while em.content.len > 0 and em.content[em.content.high] == ' ':
+          setLen(em.content, em.content.len-1)
+        wr("\L")
+        for i in 2..tok.line - em.lastLineNumber: wr("\L")
+        em.col = 0
+        for i in 1..tok.indent:
+          wr(" ")
+        em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em): wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+
+    case tok.tokType
+    of tkAnd: rememberSplit(splitAnd)
+    of tkOr: rememberSplit(splitOr)
+    of tkIn: rememberSplit(splitIn)
+    else: discard
+
+  of tkColon:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkSemicolon,
+     tkComma:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+    rememberSplit(splitComma)
+  of tkParLe, tkParRi, tkBracketLe,
+     tkBracketRi, tkCurlyLe, tkCurlyRi,
+     tkBracketDotLe, tkBracketDotRi,
+     tkCurlyDotLe, tkCurlyDotRi,
+     tkParDotLe, tkParDotRi,
+     tkColonColon, tkDot, tkBracketLeColon:
+    wr(TokTypeToStr[tok.tokType])
+    if tok.tokType in splitters:
+      rememberSplit(splitParLe)
+  of tkEquals:
+    if not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkOpr, tkDotDot:
+    if not em.endsInWhite: wr(" ")
+    wr(tok.ident.s)
+    template isUnary(tok): bool =
+      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+
+    if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
+      wr(" ")
+      rememberSplit(splitBinary)
+  of tkAccent:
+    wr(TokTypeToStr[tok.tokType])
+    em.inquote = not em.inquote
+  of tkComment:
+    if not preventComment:
+      emitComment(em, tok)
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
+    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+    softLinebreak(em, lit)
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    wr lit
+  of tkEof: discard
+  else:
+    let lit = if tok.ident != nil: tok.ident.s else: tok.literal
+    softLinebreak(em, lit)
+    if endsInAlpha(em): wr(" ")
+    wr lit
+
+  em.lastTok = tok.tokType
+  em.lastLineNumber = tok.line + em.lineSpan
+  em.lineSpan = 0
+
+proc starWasExportMarker*(em: var Emitter) =
+  if em.content.endsWith(" * "):
+    setLen(em.content, em.content.len-3)
+    em.content.add("*")
+    dec em.col, 2
+
+type
+  Thing = ref object
+    grade: string
+    # this name is great
+    name: string
+
+proc f() =
+  var c: char
+  var str: string
+  if c == '\\':
+    # escape char
+    str &= c
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
new file mode 100644
index 000000000..95071fce3
--- /dev/null
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -0,0 +1,325 @@
+discard """
+  outputsub: '''ObjectAssignmentError'''
+  exitcode: "1"
+"""
+
+import verylongnamehere, verylongnamehere,
+  verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
+
+proc `[]=`() = discard "index setter"
+proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
+
+(not false)
+
+let expr = if true: "true" else: "false"
+
+var body = newNimNode(nnkIfExpr).add(
+  newNimNode(nnkElifBranch).add(
+    infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"),
+        ident("kind"))),
+    condition
+  ),
+  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident(
+      "false"))))
+)
+
+# comment
+
+var x = 1
+
+type
+  GeneralTokenizer* = object of RootObj ## comment here
+    kind*: TokenClass         ## and here
+    start*, length*: int      ## you know how it goes...
+    buf: cstring
+    pos: int                  # other comment here.
+    state: TokenClass
+
+var x*: string
+var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]#
+
+echo "#", x, "##", y, "#" & "string" & $test
+
+echo (tup, here)
+echo(argA, argB)
+
+import macros
+
+## A documentation comment here.
+## That spans multiple lines.
+## And is not to be touched.
+
+const numbers = [4u8, 5'u16, 89898_00]
+
+macro m(n): untyped =
+  result = foo"string literal"
+
+{.push m.}
+proc p() = echo "p", 1+4 * 5, if true: 5 else: 6
+proc q(param: var ref ptr string) =
+  p()
+  if true:
+    echo a and b or not c and not -d
+{.pop.}
+
+q()
+
+when false:
+  # bug #4766
+  type
+    Plain = ref object
+      discard
+
+    Wrapped[T] = object
+      value: T
+
+  converter toWrapped[T](value: T): Wrapped[T] =
+    Wrapped[T](value: value)
+
+  let result = Plain()
+  discard $result
+
+when false:
+  # bug #3670
+  template someTempl(someConst: bool) =
+    when someConst:
+      var a: int
+    if true:
+      when not someConst:
+        var a: int
+      a = 5
+
+  someTempl(true)
+
+
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Layouter for nimpretty. Still primitive but useful.
+
+import idents, lexer, lineinfos, llstream, options, msgs, strutils
+from os import changeFileExt
+
+const
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+type
+  SplitKind = enum
+    splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
+
+  Emitter* = object
+    f: PLLStream
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote {.pragmaHereWrongCurlyEnd.}: bool
+    col, lastLineNumber, lineSpan, indentLevel: int
+    content: string
+    fixedUntil: int           # marks where we must not go in the content
+    altSplitPos: array[SplitKind, int] # alternative split positions
+
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef;
+    fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd.} =
+  let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
+  em.f = llStreamOpen(outfile, fmWrite)
+  em.config = config
+  em.fid = fileIdx
+  em.lastTok = tkInvalid
+  em.inquote = false
+  em.col = 0
+  em.content = newStringOfCap(16_000)
+  if em.f == nil:
+    rawMessage(config, errGenerated, "cannot open file: " & outfile)
+
+proc closeEmitter*(em: var Emitter) {.inline.} =
+  em.f.llStreamWrite em.content
+  llStreamClose(em.f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i+1] == '\L': inc result
+
+proc calcCol(em: var Emitter; s: string) =
+  var i = s.len-1
+  em.col = 0
+  while i >= 0 and s[i] != '\L':
+    dec i
+    inc em.col
+
+template wr(x) =
+  em.content.add x
+  inc em.col, x.len
+
+template goodCol(col): bool = col in 40..MaxLineLen
+
+const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                   tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                   tkCurlyLe}
+
+template rememberSplit(kind) =
+  if goodCol(em.col):
+    em.altSplitPos[kind] = em.content.len
+
+proc softLinebreak(em: var Emitter; lit: string) =
+  # XXX Use an algorithm that is outlined here:
+  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
+  # +2 because we blindly assume a comma or ' &' might follow
+  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
+    if em.lastTok in splitters:
+      wr("\L")
+      em.col = 0
+      for i in 1..em.indentLevel+2: wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          let ws = "\L" & repeat(' ', em.indentLevel+2)
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA,
+        tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    if not endsInWhite(em):
+      wr(" ")
+      if em.lineSpan == 0 and max(em.col,
+          LineCommentColumn) + lit.len <= MaxLineLen:
+        for i in 1 .. LineCommentColumn - em.col: wr(" ")
+    wr lit
+
+  var preventComment = case tok.tokType
+    of tokKeywordLow..tokKeywordHigh:
+      if endsInAlpha(em): wr(" ")
+      wr(TokTypeToStr[tok.tokType])
+
+      case tok.tokType
+      of tkAnd: rememberSplit(splitAnd)
+      of tkOr: rememberSplit(splitOr)
+      of tkIn: rememberSplit(splitIn)
+      else: 90
+    else:
+      "case returns value"
+
+
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber and
+      tok.indent >= 0:
+    # we have an inline comment so handle it before the indentation token:
+    emitComment(em, tok)
+    preventComment = true
+    em.fixedUntil = em.content.high
+
+  elif tok.indent >= 0:
+    em.indentLevel = tok.indent
+    # remove trailing whitespace:
+    while em.content.len > 0 and em.content[em.content.high] == ' ':
+      setLen(em.content, em.content.len-1)
+    wr("\L")
+    for i in 2..tok.line - em.lastLineNumber: wr("\L")
+    em.col = 0
+    for i in 1..tok.indent:
+      wr(" ")
+    em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em): wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+
+    case tok.tokType
+    of tkAnd: rememberSplit(splitAnd)
+    of tkOr: rememberSplit(splitOr)
+    of tkIn: rememberSplit(splitIn)
+    else: discard
+
+  of tkColon:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkSemicolon,
+     tkComma:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+    rememberSplit(splitComma)
+  of tkParLe, tkParRi, tkBracketLe,
+     tkBracketRi, tkCurlyLe, tkCurlyRi,
+     tkBracketDotLe, tkBracketDotRi,
+     tkCurlyDotLe, tkCurlyDotRi,
+     tkParDotLe, tkParDotRi,
+     tkColonColon, tkDot, tkBracketLeColon:
+    wr(TokTypeToStr[tok.tokType])
+    if tok.tokType in splitters:
+      rememberSplit(splitParLe)
+  of tkEquals:
+    if not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkOpr, tkDotDot:
+    if not em.endsInWhite: wr(" ")
+    wr(tok.ident.s)
+    template isUnary(tok): bool =
+      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+
+    if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
+      wr(" ")
+      rememberSplit(splitBinary)
+  of tkAccent:
+    wr(TokTypeToStr[tok.tokType])
+    em.inquote = not em.inquote
+  of tkComment:
+    if not preventComment:
+      emitComment(em, tok)
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit,
+      tkGTripleStrLit, tkCharLit:
+    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+    softLinebreak(em, lit)
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(
+        " ")
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    wr lit
+  of tkEof: discard
+  else:
+    let lit = if tok.ident != nil: tok.ident.s else: tok.literal
+    softLinebreak(em, lit)
+    if endsInAlpha(em): wr(" ")
+    wr lit
+
+  em.lastTok = tok.tokType
+  em.lastLineNumber = tok.line + em.lineSpan
+  em.lineSpan = 0
+
+proc starWasExportMarker*(em: var Emitter) =
+  if em.content.endsWith(" * "):
+    setLen(em.content, em.content.len-3)
+    em.content.add("*")
+    dec em.col, 2
+
+type
+  Thing = ref object
+    grade: string
+    # this name is great
+    name: string
+
+proc f() =
+  var c: char
+  var str: string
+  if c == '\\':
+    # escape char
+    str &= c
diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim
new file mode 100644
index 000000000..6a4877b2c
--- /dev/null
+++ b/tests/arithm/tnot.nim
@@ -0,0 +1,58 @@
+discard """
+  output: '''
+-5
+-5
+-5
+-5
+4
+4
+4
+4
+251
+65531
+4294967291
+18446744073709551611
+4
+4
+4
+4
+'''
+"""
+
+# Signed types
+block:
+  const t0: int8  = not 4
+  const t1: int16 = not 4
+  const t2: int32 = not 4
+  const t3: int64 = not 4
+  const t4: int8  = not -5
+  const t5: int16 = not -5
+  const t6: int32 = not -5
+  const t7: int64 = not -5
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+  echo t4
+  echo t5
+  echo t6
+  echo t7
+
+# Unsigned types
+block:
+  const t0: uint8  = not 4'u8
+  const t1: uint16 = not 4'u16
+  const t2: uint32 = not 4'u32
+  const t3: uint64 = not 4'u64
+  const t4: uint8  = not 251'u8
+  const t5: uint16 = not 65531'u16
+  const t6: uint32 = not 4294967291'u32
+  const t7: uint64 = not 18446744073709551611'u64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+  echo t4
+  echo t5
+  echo t6
+  echo t7
diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim
new file mode 100644
index 000000000..0aa46d021
--- /dev/null
+++ b/tests/arithm/tshl.nim
@@ -0,0 +1,34 @@
+discard """
+  output: '''
+0
+0
+1
+1
+0
+0
+0
+1
+'''
+"""
+
+# Signed types
+block:
+  const t0: int8  = 1'i8 shl 8
+  const t1: int16 = 1'i16 shl 16
+  const t2: int32 = 1'i32 shl 32
+  const t3: int64 = 1'i64 shl 64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+
+# Unsigned types
+block:
+  const t0: uint8  = 1'u8 shl 8
+  const t1: uint16 = 1'u16 shl 16
+  const t2: uint32 = 1'u32 shl 32
+  const t3: uint64 = 1'u64 shl 64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim
new file mode 100644
index 000000000..6ebc077d9
--- /dev/null
+++ b/tests/concepts/treversable.nim
@@ -0,0 +1,31 @@
+# issue 7705, 7703, 7702
+discard """
+  output: '''
+z
+e
+  '''
+"""
+
+type
+  Reversable*[T] = concept a
+    a[int] is T
+    a.high is int
+    a.len is int
+    a.low is int
+
+proc get[T](s: Reversable[T], n: int): T =
+  s[n]
+
+proc hi[T](s: Reversable[T]): int =
+  s.high
+
+proc lo[T](s: Reversable[T]): int =
+  s.low
+
+iterator reverse*[T](s: Reversable[T]): T =
+  assert hi(s) - lo(s) == len(s) - 1
+  for z in hi(s).countdown(lo(s)):
+    yield s.get(z)
+
+for s in @["e", "z"].reverse:
+  echo s
diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim
new file mode 100644
index 000000000..688180fd2
--- /dev/null
+++ b/tests/niminaction/Chapter1/various1.nim
@@ -0,0 +1,45 @@
+discard """
+  exitCode: 0
+  outputsub: "Woof!"
+"""
+
+import strutils
+echo("hello".to_upper())
+echo("world".toUpper())
+
+type
+  Dog = object #<1>
+    age: int #<2>
+
+let dog = Dog(age: 3) #<3>
+
+proc showNumber(num: int | float) =
+  echo(num)
+
+showNumber(3.14)
+showNumber(42)
+
+for i in 0 .. <10:
+  echo(i)
+
+block: # Block added due to clash.
+  type
+    Dog = object
+
+  proc bark(self: Dog) = #<1>
+    echo("Woof!")
+
+  let dog = Dog()
+  dog.bark() #<2>
+
+import sequtils, future, strutils
+let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"]
+list.map(
+  (x: string) -> (string, string) => (x.split[0], x.split[1])
+).echo
+
+import strutils
+let list1 = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"]
+for name in list1:
+  echo((name.split[0], name.split[1]))
+
diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim
new file mode 100644
index 000000000..3e94c335b
--- /dev/null
+++ b/tests/niminaction/Chapter2/explicit_discard.nim
@@ -0,0 +1,7 @@
+discard """
+  line: 7
+  errormsg: "has to be discarded"
+"""
+
+proc myProc(name: string): string = "Hello " & name
+myProc("Dominik")
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim
new file mode 100644
index 000000000..77f0a7dd8
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_def_eq.nim
@@ -0,0 +1,16 @@
+discard """
+  line: 16
+  errormsg: "type mismatch"
+"""
+
+type
+    Dog = object
+      name: string
+
+    Cat = object
+      name: string
+
+let dog: Dog = Dog(name: "Fluffy")
+let cat: Cat = Cat(name: "Fluffy")
+
+echo(dog == cat)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim
new file mode 100644
index 000000000..331d69480
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_iterator.nim
@@ -0,0 +1,7 @@
+discard """
+  line: 6
+  errormsg: "type mismatch"
+"""
+
+for i in 5:
+  echo i
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim
new file mode 100644
index 000000000..493be270a
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_seq_type.nim
@@ -0,0 +1,6 @@
+discard """
+  line: 6
+  errormsg: "cannot infer the type of the sequence"
+"""
+
+var list = @[]
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim
new file mode 100644
index 000000000..7dd976b40
--- /dev/null
+++ b/tests/niminaction/Chapter2/resultaccept.nim
@@ -0,0 +1,28 @@
+discard """
+  output: ""
+"""
+
+# Page 35.
+
+proc implicit: string =
+  "I will be returned"
+  
+proc discarded: string =
+  discard "I will not be returned"
+  
+proc explicit: string =
+  return "I will be returned"
+
+proc resultVar: string =
+  result = "I will be returned"
+  
+proc resultVar2: string =
+  result = ""
+  result.add("I will be ")
+  result.add("returned")
+
+doAssert implicit() == "I will be returned"
+doAssert discarded() == nil
+doAssert explicit() == "I will be returned"
+doAssert resultVar() == "I will be returned"
+doAssert resultVar2() == "I will be returned"
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim
new file mode 100644
index 000000000..de59af7d9
--- /dev/null
+++ b/tests/niminaction/Chapter2/resultreject.nim
@@ -0,0 +1,33 @@
+discard """
+  line: 27
+  errormsg: "has to be discarded"
+"""
+
+# Page 35.
+
+proc implicit: string =
+  "I will be returned"
+  
+proc discarded: string =
+  discard "I will not be returned"
+
+proc explicit: string =
+  return "I will be returned"
+
+proc resultVar: string =
+  result = "I will be returned"
+  
+proc resultVar2: string =
+  result = ""
+  result.add("I will be ")
+  result.add("returned")
+
+proc resultVar3: string =
+  result = "I am the result"
+  "I will cause an error"
+
+doAssert implicit() == "I will be returned"
+doAssert discarded() == nil
+doAssert explicit() == "I will be returned"
+doAssert resultVar() == "I will be returned"
+doAssert resultVar2() == "I will be returned"
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim
new file mode 100644
index 000000000..3f6a3f453
--- /dev/null
+++ b/tests/niminaction/Chapter2/various2.nim
@@ -0,0 +1,369 @@
+discard """
+  exitCode: 0
+  outputsub: '''42 is greater than 0'''
+"""
+
+if 42 >= 0:
+  echo "42 is greater than 0"
+
+
+echo("Output: ",
+  5)
+echo(5 +
+  5)
+# --- Removed code that is supposed to fail here. Not going to test those. ---
+
+# Single-line comment
+#[
+Multiline comment
+]#
+when false:
+  echo("Commented-out code")
+
+let decimal = 42
+let hex = 0x42
+let octal = 0o42
+let binary = 0b101010
+
+let a: int16 = 42
+let b = 42'i8
+
+let c = 1'f32 # --- Changed names here to avoid clashes ---
+let d = 1.0e19
+
+let e = false
+let f = true
+
+let g = 'A'
+let h = '\109'
+let i = '\x79'
+
+let text = "The book title is \"Nim in Action\""
+
+let filepath = "C:\\Program Files\\Nim"
+
+# --- Changed name here to avoid clashes ---
+let filepath1 = r"C:\Program Files\Nim"
+
+let multiLine = """foo
+  bar
+  baz
+"""
+echo multiLine
+
+import strutils
+# --- Changed name here to avoid clashes ---
+let multiLine1 = """foo
+  bar
+  baz
+"""
+echo multiLine1.unindent
+doAssert multiLine1.unindent == "foo\nbar\nbaz\n"
+
+proc fillString(): string =
+  result = ""
+  echo("Generating string")
+  for i in 0 .. 4:
+    result.add($i) #<1>
+
+const count = fillString()
+
+var
+  text1 = "hello"
+  number: int = 10
+  isTrue = false
+
+var 火 = "Fire"
+let ogień = true
+
+var `var` = "Hello"
+echo(`var`)
+
+proc myProc(name: string): string = "Hello " & name
+discard myProc("Dominik")
+
+proc bar(): int #<1>
+
+proc foo(): float = bar().float
+proc bar(): int = foo().int
+
+proc noReturn() = echo("Hello")
+proc noReturn2(): void = echo("Hello")
+
+proc noReturn3 = echo("Hello")
+
+proc message(recipient: string): auto =
+  "Hello " & recipient
+
+doAssert message("Dom") == "Hello Dom"
+
+proc max(a: int, b: int): int =
+  if a > b: a else: b
+
+doAssert max(5, 10) == 10
+
+proc max2(a, b: int): int =
+  if a > b: a else: b
+
+proc genHello(name: string, surname = "Doe"): string =
+  "Hello " & name & " " & surname
+
+# -- Leaving these as asserts as that is in the original code, just in case
+# -- somehow in the future `assert` is removed :)
+assert genHello("Peter") == "Hello Peter Doe"
+assert genHello("Peter", "Smith") == "Hello Peter Smith"
+
+proc genHello2(names: varargs[string]): string =
+  result = ""
+  for name in names:
+    result.add("Hello " & name & "\n")
+
+doAssert genHello2("John", "Bob") == "Hello John\nHello Bob\n"
+
+proc getUserCity(firstName, lastName: string): string =
+  case firstName
+  of "Damien": return "Tokyo"
+  of "Alex": return "New York"
+  else: return "Unknown"
+
+proc getUserCity(userID: int): string =
+  case userID
+  of 1: return "Tokyo"
+  of 2: return "New York"
+  else: return "Unknown"
+
+doAssert getUserCity("Damien", "Lundi") == "Tokyo"
+doAssert getUserCity(2) == "New York" # -- Errata here: missing closing "
+
+import sequtils
+let numbers = @[1, 2, 3, 4, 5, 6]
+let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0)
+doAssert odd == @[1, 3, 5]
+
+import sequtils, future
+let numbers1 = @[1, 2, 3, 4, 5, 6]
+let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0)
+assert odd1 == @[1, 3, 5]
+
+proc isValid(x: int, validator: proc (x: int): bool) =
+  if validator(x): echo(x, " is valid")
+  else: echo(x, " is NOT valid")
+
+import future
+proc isValid2(x: int, validator: (x: int) -> bool) =
+  if validator(x): echo(x, " is valid")
+  else: echo(x, " is NOT valid")
+
+var list: array[3, int]
+list[0] = 1
+list[1] = 42
+assert list[0] == 1
+assert list[1] == 42
+assert list[2] == 0 #<1>
+
+echo list.repr #<2>
+
+# echo list[500]
+
+var list2: array[-10 .. -9, int]
+list2[-10] = 1
+list2[-9] = 2
+
+var list3 = ["Hi", "There"]
+
+var list4 = ["My", "name", "is", "Dominik"]
+for item in list4:
+  echo(item)
+
+for i in list4.low .. list4.high:
+  echo(list4[i])
+
+var list5: seq[int] = @[]
+doAssertRaises(IndexError):
+  list5[0] = 1
+
+list5.add(1)
+
+assert list5[0] == 1
+doAssertRaises(IndexError):
+  echo list5[42]
+
+# -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception,
+# --         not a SIGSEGV.
+
+block:
+  var list = newSeq[string](3)
+  assert list[0] == nil
+  list[0] = "Foo"
+  list[1] = "Bar"
+  list[2] = "Baz"
+
+  list.add("Lorem")
+
+block:
+  let list = @[4, 8, 15, 16, 23, 42]
+  for i in 0 .. <list.len:
+    stdout.write($list[i] & " ")
+
+var collection: set[int16]
+doAssert collection == {}
+
+block:
+  let collection = {'a', 'x', 'r'}
+  doAssert 'a' in collection
+
+block:
+  let collection = {'a', 'T', 'z'}
+  let isAllLowerCase = {'A' .. 'Z'} * collection == {}
+  doAssert(not isAllLowerCase)
+
+let age = 10
+if age > 0 and age <= 10:
+  echo("You're still a child")
+elif age > 10 and age < 18:
+  echo("You're a teenager")
+else:
+  echo("You're an adult")
+
+let variable = "Arthur"
+case variable
+of "Arthur", "Zaphod", "Ford":
+  echo("Male")
+of "Marvin":
+  echo("Robot")
+of "Trillian":
+  echo("Female")
+else:
+  echo("Unknown")
+
+let ageDesc = if age < 18: "Non-Adult" else: "Adult"
+
+block:
+  var i = 0
+  while i < 3:
+    echo(i)
+    i.inc
+
+block label:
+  var i = 0
+  while true:
+    while i < 5:
+      if i > 3: break label
+      i.inc
+
+iterator values(): int =
+  var i = 0
+  while i < 5:
+    yield i
+    i.inc
+
+for value in values():
+  echo(value)
+
+import os
+for filename in walkFiles("*.nim"):
+  echo(filename)
+
+for item in @[1, 2, 3]:
+  echo(item)
+
+for i, value in @[1, 2, 3]: echo("Value at ", i, ": ", value)
+
+doAssertRaises(IOError):
+  proc second() =
+    raise newException(IOError, "Somebody set us up the bomb")
+
+  proc first() =
+    second()
+
+  first()
+
+block:
+  proc second() =
+    raise newException(IOError, "Somebody set us up the bomb")
+
+  proc first() =
+    try:
+      second()
+    except:
+      echo("Cannot perform second action because: " &
+        getCurrentExceptionMsg())
+
+  first()
+
+block:
+  type
+    Person = object
+      name: string
+      age: int
+
+  var person: Person
+  var person1 = Person(name: "Neo", age: 28)
+
+block:
+  type
+    PersonObj = object
+      name: string
+      age: int
+    PersonRef = ref PersonObj
+
+  # proc setName(person: PersonObj) =
+  #   person.name = "George"
+
+  proc setName(person: PersonRef) =
+    person.name = "George"
+
+block:
+  type
+    Dog = object
+      name: string
+
+    Cat = object
+      name: string
+
+  let dog: Dog = Dog(name: "Fluffy")
+  let cat: Cat = Cat(name: "Fluffy")
+
+block:
+  type
+    Dog = tuple
+      name: string
+
+    Cat = tuple
+      name: string
+
+  let dog: Dog = (name: "Fluffy")
+  let cat: Cat = (name: "Fluffy")
+
+  echo(dog == cat)
+
+block:
+  type
+    Point = tuple[x, y: int]
+    Point2 = (int, int)
+
+  let pos: Point = (x: 100, y: 50)
+  doAssert pos == (100, 50)
+
+  let pos1: Point = (x: 100, y: 50)
+  let (x, y) = pos1 #<1>
+  let (left, _) = pos1
+  doAssert x == pos1[0]
+  doAssert y == pos1[1]
+  doAssert left == x
+
+block:
+  type
+    Color = enum
+      colRed,
+      colGreen,
+      colBlue
+
+  let color: Color = colRed
+
+block:
+  type
+    Color {.pure.} = enum
+      red, green, blue
+
+  let color = Color.red
\ No newline at end of file
diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim
new file mode 100644
index 000000000..478229b00
--- /dev/null
+++ b/tests/niminaction/Chapter3/various3.nim
@@ -0,0 +1,93 @@
+import threadpool
+proc foo: string = "Dog"
+var x: FlowVar[string] = spawn foo()
+assert(^x == "Dog")
+
+block:
+  type
+    Box = object
+      case empty: bool
+      of false:
+        contents: string
+      else:
+        discard
+
+  var obj = Box(empty: false, contents: "Hello")
+  assert obj.contents == "Hello"
+
+  var obj2 = Box(empty: true)
+  doAssertRaises(FieldError):
+    echo(obj2.contents)
+
+import json
+assert parseJson("null").kind == JNull
+assert parseJson("true").kind == JBool
+assert parseJson("42").kind == JInt
+assert parseJson("3.14").kind == JFloat
+assert parseJson("\"Hi\"").kind == JString
+assert parseJson("""{ "key": "value" }""").kind == JObject
+assert parseJson("[1, 2, 3, 4]").kind == JArray
+
+import json
+let data = """
+  {"username": "Dominik"}
+"""
+
+let obj = parseJson(data) 
+assert obj.kind == JObject 
+assert obj["username"].kind == JString 
+assert obj["username"].str == "Dominik"
+
+block:
+  proc count10(): int =
+    for i in 0 .. <10:
+      result.inc
+  assert count10() == 10
+
+type
+  Point = tuple[x, y: int]
+
+var point = (5, 10)
+var point2 = (x: 5, y: 10)
+
+type
+  Human = object
+    name: string
+    age: int
+
+var jeff = Human(name: "Jeff", age: 23)
+var amy = Human(name: "Amy", age: 20)
+
+import asyncdispatch
+
+var future = newFuture[int]() 
+doAssert(not future.finished) 
+
+future.callback = 
+  proc (future: Future[int]) = 
+    echo("Future is no longer empty, ", future.read) 
+
+future.complete(42)
+
+import asyncdispatch, asyncfile
+
+when false:
+  var file = openAsync("")
+  let dataFut = file.readAll()
+  dataFut.callback =
+    proc (future: Future[string]) =
+      echo(future.read())
+
+  asyncdispatch.runForever()
+
+import asyncdispatch, asyncfile, os
+
+proc readFiles() {.async.} =
+  # --- Changed to getTempDir here.
+  var file = openAsync(getTempDir() / "test.txt", fmReadWrite)
+  let data = await file.readAll() 
+  echo(data) 
+  await file.write("Hello!\n") 
+
+waitFor readFiles()
+
diff --git a/tests/niminaction/Chapter3/various3.nim.cfg b/tests/niminaction/Chapter3/various3.nim.cfg
new file mode 100644
index 000000000..6c1ded992
--- /dev/null
+++ b/tests/niminaction/Chapter3/various3.nim.cfg
@@ -0,0 +1 @@
+threads:on
\ No newline at end of file
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
new file mode 100644
index 000000000..e5b709a66
--- /dev/null
+++ b/tests/stdlib/tpegs.nim
@@ -0,0 +1,78 @@
+discard """
+  output: '''
+pkNonTerminal: Sum @(2, 3)
+  pkSequence: (Product (('+' / '-') Product)*)
+    pkNonTerminal: Product @(3, 7)
+      pkSequence: (Value (('*' / '/') Value)*)
+        pkNonTerminal: Value @(4, 5)
+          pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')'))
+            pkSequence: ([0-9] [0-9]*)
+              pkCharChoice: [0-9]
+              pkGreedyRepSet: [0-9]*
+            pkSequence: ('(' Expr ')')
+              pkChar: '('
+              pkNonTerminal: Expr @(1, 4)
+                pkNonTerminal: Sum @(2, 3)
+              pkChar: ')'
+        pkGreedyRep: (('*' / '/') Value)*
+          pkSequence: (('*' / '/') Value)
+            pkOrderedChoice: ('*' / '/')
+              pkChar: '*'
+              pkChar: '/'
+            pkNonTerminal: Value @(4, 5)
+    pkGreedyRep: (('+' / '-') Product)*
+      pkSequence: (('+' / '-') Product)
+        pkOrderedChoice: ('+' / '-')
+          pkChar: '+'
+          pkChar: '-'
+        pkNonTerminal: Product @(3, 7)
+'''
+"""
+
+import strutils, streams
+import pegs
+
+const
+  indent = "  "
+
+let
+  pegSrc = """
+Expr <- Sum
+Sum <- Product (('+' / '-') Product)*
+Product <- Value (('*' / '/') Value)*
+Value <- [0-9]+ / '(' Expr ')'
+  """
+  pegAst: Peg = pegSrc.peg
+
+var
+  outp = newStringStream()
+  processed: seq[string] = @[]
+
+proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) =
+  outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s]
+
+proc recLoop(p: Peg, level: int = 0) =
+  case p.kind
+  of pkEmpty..pkWhitespace:
+    discard
+  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  of pkChar, pkGreedyRepChar:
+    outp.prt(p.kind, $p, level)
+  of pkCharChoice, pkGreedyRepSet:
+    outp.prt(p.kind, $p, level)
+  of pkNonTerminal:
+    outp.prt(p.kind,
+      "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level)
+    if not(p.nt.name in processed):
+      processed.add p.nt.name
+      p.nt.rule.recLoop level+1
+  of pkBackRef..pkBackRefIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  else:
+    outp.prt(p.kind, $p, level)
+    for s in items(p):
+      s.recLoop level+1
+
+pegAst.recLoop
+echo outp.data
\ No newline at end of file
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 84e536636..9affbc159 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -266,8 +266,17 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP
 
   let tests = [
+    "niminaction/Chapter1/various1",
+    "niminaction/Chapter2/various2",
+    "niminaction/Chapter2/resultaccept",
+    "niminaction/Chapter2/resultreject",
+    "niminaction/Chapter2/explicit_discard",
+    "niminaction/Chapter2/no_def_eq",
+    "niminaction/Chapter2/no_iterator",
+    "niminaction/Chapter2/no_seq_type",
     "niminaction/Chapter3/ChatApp/src/server",
     "niminaction/Chapter3/ChatApp/src/client",
+    "niminaction/Chapter3/various3",
     "niminaction/Chapter6/WikipediaStats/concurrency_regex",
     "niminaction/Chapter6/WikipediaStats/concurrency",
     "niminaction/Chapter6/WikipediaStats/naive",
@@ -278,8 +287,34 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     "niminaction/Chapter7/Tweeter/src/tweeter",
     "niminaction/Chapter7/Tweeter/src/createDatabase",
     "niminaction/Chapter7/Tweeter/tests/database_test",
-    "niminaction/Chapter8/sdl/sdl_test",
+    "niminaction/Chapter8/sdl/sdl_test"
     ]
+
+  # Verify that the files have not been modified. Death shall fall upon
+  # whoever edits these hashes without dom96's permission, j/k. But please only
+  # edit when making a conscious breaking change, also please try to make your
+  # commit message clear and notify me so I can easily compile an errata later.
+  var testHashes: seq[string] = @[]
+
+  for test in tests:
+    testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string))
+
+  const refHashes = @[
+    "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf",
+    "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81",
+    "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228",
+    "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830",
+    "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b",
+    "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184",
+    "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770",
+    "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e",
+    "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02",
+    "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8",
+    "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127"
+  ]
+  doAssert testHashes == refHashes, "Nim in Action tests were changed."
+
+  # Run the tests.
   for testfile in tests:
     test "tests/" & testfile & ".nim", actionCompile
 
@@ -291,6 +326,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
 
 
 
+
 # ------------------------- manyloc -------------------------------------------
 #proc runSpecialTests(r: var TResults, options: string) =
 #  for t in ["lib/packages/docutils/highlite"]:
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 0185156ec..0764b6363 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -129,7 +129,7 @@ proc callCCompiler(cmdTemplate, filename, options: string,
   let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
                        "options", options, "file", filename.quoteShell,
                        "filedir", filename.getFileDir()])
-  var p = startProcess(command="gcc", args=c[5.. ^1],
+  var p = startProcess(command="gcc", args=c[5 .. ^1],
                        options={poStdErrToStdOut, poUsePath})
   let outp = p.outputStream
   var x = newStringOfCap(120)
diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim
new file mode 100644
index 000000000..ef75bb86c
--- /dev/null
+++ b/tests/types/t7905.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+(member: "hello world")
+(member: 123.456)
+(member: "hello world", x: ...)
+(member: 123.456, x: ...)
+'''
+"""
+
+template foobar(arg: typed): untyped =
+  type
+    MyType = object
+      member: type(arg)
+
+  var myVar: MyType
+  myVar.member = arg
+  echo myVar
+
+foobar("hello world")
+foobar(123.456'f64)
+
+template foobarRec(arg: typed): untyped =
+  type
+    MyType = object
+      member: type(arg)
+      x: ref MyType
+
+  var myVar: MyType
+  myVar.member = arg
+  echo myVar
+
+foobarRec("hello world")
+foobarRec(123.456'f64)