summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--appveyor.yml2
-rw-r--r--changelog.md3
-rw-r--r--compiler/ccgstmts.nim21
-rw-r--r--compiler/layouter.nim268
-rw-r--r--compiler/lexer.nim26
-rw-r--r--compiler/parser.nim42
-rw-r--r--compiler/pragmas.nim129
-rw-r--r--compiler/renderer.nim5
-rw-r--r--compiler/semstmts.nim6
-rw-r--r--doc/tut1.rst5
-rw-r--r--koch.nim8
-rw-r--r--lib/pure/pegs.nim23
-rw-r--r--lib/pure/strutils.nim4
-rw-r--r--lib/pure/terminal.nim91
-rw-r--r--nimpretty/nimpretty.nim (renamed from tools/nimpretty.nim)20
-rw-r--r--nimpretty/nimpretty.nim.cfg2
-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/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/overflw/toverflw.nim75
-rw-r--r--tests/stdlib/tpegs.nim78
-rw-r--r--tests/testament/categories.nim38
-rw-r--r--tests/testament/tester.nim2
34 files changed, 1998 insertions, 127 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 4067cb693..4a490dfdd 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/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/layouter.nim b/compiler/layouter.nim
new file mode 100644
index 000000000..62844db4b
--- /dev/null
+++ b/compiler/layouter.nim
@@ -0,0 +1,268 @@
+#
+#
+#           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.
+
+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
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote: bool
+    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, 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)
+  em.indentStack = newSeqOfCap[int](30)
+  em.indentStack.add 0
+
+proc closeEmitter*(em: var Emitter) =
+  var f = llStreamOpen(em.config.outFile, fmWrite)
+  if f == nil:
+    rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile)
+  f.llStreamWrite em.content
+  llStreamClose(f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i] == '\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
+  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+moreIndent(em): wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          var spaces = 0
+          while a+spaces < em.content.len and em.content[a+spaces] == ' ':
+            inc spaces
+          if spaces > 0: delete(em.content, a, a+spaces-1)
+          let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em))
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len == 0 or em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    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 = false
+  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:
+    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..em.indentLevel:
+      wr(" ")
+    em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em):
+      wr(" ")
+    elif not em.inquote and not endsInWhite(em) and
+        em.lastTok notin openPars:
+      #and tok.tokType in oprSet
+      wr(" ")
+
+    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
+    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])
+    rememberSplit(splitComma)
+    wr(" ")
+  of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe,
+     tkCurlyLe, tkCurlyDotLe, tkBracketLeColon:
+    if tok.strongSpaceA > 0 and not em.endsInWhite:
+      wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    rememberSplit(splitParLe)
+  of tkParRi,
+     tkBracketRi, tkCurlyRi,
+     tkBracketDotRi,
+     tkCurlyDotRi,
+     tkParDotRi,
+     tkColonColon, tkDot:
+    wr(TokTypeToStr[tok.tokType])
+  of tkEquals:
+    if not em.inquote and not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    if not em.inquote: wr(" ")
+  of tkOpr, tkDotDot:
+    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):
+        wr(" ")
+        rememberSplit(splitBinary)
+  of tkAccent:
+    if not em.inquote and endsInAlpha(em): wr(" ")
+    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
+
+proc commaWasSemicolon*(em: var Emitter) =
+  if 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 f2af3f51c..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:
@@ -949,6 +949,8 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
       if isDoc or defined(nimpretty): tok.literal.add buf[pos]
       inc(pos)
   L.bufpos = pos
+  when defined(nimpretty):
+    tok.commentOffsetB = L.offsetBase + pos - 1
 
 proc scanComment(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
@@ -957,6 +959,9 @@ proc scanComment(L: var TLexer, tok: var TToken) =
   # iNumber contains the number of '\n' in the token
   tok.iNumber = 0
   assert buf[pos+1] == '#'
+  when defined(nimpretty):
+    tok.commentOffsetA = L.offsetBase + pos - 1
+
   if buf[pos+2] == '[':
     skipMultiLineComment(L, tok, pos+3, true)
     return
@@ -996,6 +1001,8 @@ proc scanComment(L: var TLexer, tok: var TToken) =
       tokenEndIgnore(tok, pos)
       break
   L.bufpos = pos
+  when defined(nimpretty):
+    tok.commentOffsetB = L.offsetBase + pos - 1
 
 proc skip(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
@@ -1016,6 +1023,9 @@ proc skip(L: var TLexer, tok: var TToken) =
       inc(pos)
     of CR, LF:
       tokenEndPrevious(tok, pos)
+      when defined(nimpretty):
+        # we are not yet in a comment, so update the comment token's line information:
+        if not hasComment: inc tok.line
       pos = handleCRLF(L, pos)
       buf = L.buf
       var indent = 0
@@ -1055,7 +1065,7 @@ proc skip(L: var TLexer, tok: var TToken) =
   L.bufpos = pos
   when defined(nimpretty):
     if hasComment:
-      tok.commentOffsetB = L.offsetBase + pos
+      tok.commentOffsetB = L.offsetBase + pos - 1
       tok.tokType = tkComment
     if gIndentationWidth <= 0:
       gIndentationWidth = tok.indent
@@ -1210,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 aedee8538..f575f3d7e 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -29,6 +29,9 @@ when isMainModule:
 import
   llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos
 
+when defined(nimpretty2):
+  import layouter
+
 type
   TParser* = object            # A TParser object represents a file that
                                # is being parsed
@@ -41,6 +44,8 @@ type
     inPragma*: int             # Pragma level
     inSemiStmtList*: int
     emptyNode: PNode
+    when defined(nimpretty2):
+      em: Emitter
 
   SymbolMode = enum
     smNormal, smAllowNil, smAfterDot
@@ -83,6 +88,11 @@ proc getTok(p: var TParser) =
   ## `tok` member.
   rawGetTok(p.lex, p.tok)
   p.hasProgress = true
+  when defined(nimpretty2):
+    emitTok(p.em, p.lex, p.tok)
+    while p.tok.tokType == tkComment:
+      rawGetTok(p.lex, p.tok)
+      emitTok(p.em, p.lex, p.tok)
 
 proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef;
@@ -91,6 +101,8 @@ 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, cache, config, fileIdx)
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
@@ -104,6 +116,8 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
 proc closeParser(p: var TParser) =
   ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
+  when defined(nimpretty2):
+    closeEmitter(p.em)
 
 proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
@@ -394,6 +408,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
@@ -403,6 +419,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})
@@ -823,7 +841,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
@@ -898,8 +920,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 =
@@ -907,6 +933,8 @@ proc identVis(p: var TParser; allowDot=false): PNode =
   #| identVisDot = symbol '.' optInd symbol opr?
   var a = parseSymbol(p)
   if p.tok.tokType == tkOpr:
+    when defined(nimpretty2):
+      starWasExportMarker(p.em)
     result = newNodeP(nkPostfix, p)
     addSon(result, newIdentNodeP(p.tok.ident, p))
     addSon(result, a)
@@ -984,6 +1012,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)
@@ -1018,6 +1048,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)
@@ -1037,6 +1069,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)
@@ -1050,6 +1084,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)):
@@ -1652,6 +1688,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 bfb8e78eb..c78a3519c 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -223,9 +223,9 @@ proc isTurnedOn(c: PContext, n: PNode): bool =
     if x.kind == nkIntLit: return x.intVal != 0
   localError(c.config, n.info, "'on' or 'off' expected")
 
-proc onOff(c: PContext, n: PNode, op: TOptions) =
-  if isTurnedOn(c, n): c.config.options = c.config.options + op
-  else: c.config.options = c.config.options - op
+proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
+  if isTurnedOn(c, n): resOptions = resOptions + op
+  else: resOptions = resOptions - op
 
 proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
   if isTurnedOn(c, n): incl(c.module.flags, flag)
@@ -313,54 +313,68 @@ proc processNote(c: PContext, n: PNode) =
   else:
     invalidPragma(c, n)
 
-proc processOption(c: PContext, n: PNode): bool =
-  if n.kind notin nkPragmaCallKinds or n.len != 2: result = true
+proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
+  case w
+  of wChecks: ChecksOptions
+  of wObjChecks: {optObjCheck}
+  of wFieldChecks: {optFieldCheck}
+  of wRangechecks: {optRangeCheck}
+  of wBoundchecks: {optBoundsCheck}
+  of wOverflowchecks: {optOverflowCheck}
+  of wNilchecks: {optNilCheck}
+  of wFloatchecks: {optNaNCheck, optInfCheck}
+  of wNanChecks: {optNaNCheck}
+  of wInfChecks: {optInfCheck}
+  of wMovechecks: {optMoveCheck}
+  of wAssertions: {optAssert}
+  of wWarnings: {optWarns}
+  of wHints: {optHints}
+  of wLinedir: {optLineDir}
+  of wStacktrace: {optStackTrace}
+  of wLinetrace: {optLineTrace}
+  of wDebugger: {optEndb}
+  of wProfiler: {optProfiler, optMemTracker}
+  of wMemTracker: {optMemTracker}
+  of wByRef: {optByRef}
+  of wImplicitStatic: {optImplicitStatic}
+  of wPatterns: {optPatterns}
+  else: {}
+
+proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool =
+  result = true
+  if n.kind notin nkPragmaCallKinds or n.len != 2: result = false
   elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
-  elif n.sons[0].kind != nkIdent: result = true
+  elif n.sons[0].kind != nkIdent: result = false
   else:
     let sw = whichKeyword(n.sons[0].ident)
-    case sw
-    of wChecks: onOff(c, n, ChecksOptions)
-    of wObjChecks: onOff(c, n, {optObjCheck})
-    of wFieldChecks: onOff(c, n, {optFieldCheck})
-    of wRangechecks: onOff(c, n, {optRangeCheck})
-    of wBoundchecks: onOff(c, n, {optBoundsCheck})
-    of wOverflowchecks: onOff(c, n, {optOverflowCheck})
-    of wNilchecks: onOff(c, n, {optNilCheck})
-    of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck})
-    of wNanChecks: onOff(c, n, {optNaNCheck})
-    of wInfChecks: onOff(c, n, {optInfCheck})
-    of wMovechecks: onOff(c, n, {optMoveCheck})
-    of wAssertions: onOff(c, n, {optAssert})
-    of wWarnings: onOff(c, n, {optWarns})
-    of wHints: onOff(c, n, {optHints})
-    of wCallconv: processCallConv(c, n)
-    of wLinedir: onOff(c, n, {optLineDir})
-    of wStacktrace: onOff(c, n, {optStackTrace})
-    of wLinetrace: onOff(c, n, {optLineTrace})
-    of wDebugger: onOff(c, n, {optEndb})
-    of wProfiler: onOff(c, n, {optProfiler, optMemTracker})
-    of wMemTracker: onOff(c, n, {optMemTracker})
-    of wByRef: onOff(c, n, {optByRef})
-    of wDynlib: processDynLib(c, n, nil)
-    of wOptimization:
-      if n.sons[1].kind != nkIdent:
-        invalidPragma(c, n)
-      else:
-        case n.sons[1].ident.s.normalize
-        of "speed":
-          incl(c.config.options, optOptimizeSpeed)
-          excl(c.config.options, optOptimizeSize)
-        of "size":
-          excl(c.config.options, optOptimizeSpeed)
-          incl(c.config.options, optOptimizeSize)
-        of "none":
-          excl(c.config.options, optOptimizeSpeed)
-          excl(c.config.options, optOptimizeSize)
-        else: localError(c.config, n.info, "'none', 'speed' or 'size' expected")
-    of wImplicitStatic: onOff(c, n, {optImplicitStatic})
-    of wPatterns: onOff(c, n, {optPatterns})
-    else: result = true
+    let opts = pragmaToOptions(sw)
+    if opts != {}:
+      onOff(c, n, opts, resOptions)
+    else:
+      case sw
+      of wCallconv: processCallConv(c, n)
+      of wDynlib: processDynLib(c, n, nil)
+      of wOptimization:
+        if n.sons[1].kind != nkIdent:
+          invalidPragma(c, n)
+        else:
+          case n.sons[1].ident.s.normalize
+          of "speed":
+            incl(resOptions, optOptimizeSpeed)
+            excl(resOptions, optOptimizeSize)
+          of "size":
+            excl(resOptions, optOptimizeSpeed)
+            incl(resOptions, optOptimizeSize)
+          of "none":
+            excl(resOptions, optOptimizeSpeed)
+            excl(resOptions, optOptimizeSize)
+          else: localError(c.config, n.info, "'none', 'speed' or 'size' expected")
+      else: result = false
+
+proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
+  if not tryProcessOption(c, n, resOptions):
+    # calling conventions (boring...):
+    localError(c.config, n.info, "option expected")
 
 proc processPush(c: PContext, n: PNode, start: int) =
   if n.sons[start-1].kind in nkPragmaCallKinds:
@@ -373,7 +387,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
   x.notes = c.config.notes
   c.optionStack.add(x)
   for i in countup(start, sonsLen(n) - 1):
-    if processOption(c, n.sons[i]):
+    if not tryProcessOption(c, n.sons[i], c.config.options):
       # simply store it somewhere:
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
@@ -964,13 +978,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wCodegenDecl: processCodegenDecl(c, it, sym)
       of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
          wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-         wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks,
-         wCallconv,
-         wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
-         wPatterns:
-        if processOption(c, it):
-          # calling conventions (boring...):
-          localError(c.config, it.info, "option expected")
+         wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
+         wFloatchecks, wNanChecks, wInfChecks, wPatterns:
+        processOption(c, it, c.config.options)
+      of wStacktrace, wLinetrace:
+        if sym.kind in {skProc, skMethod, skConverter}:
+          processOption(c, it, sym.options)
+        else:
+          processOption(c, it, c.config.options)
       of FirstCallConv..LastCallConv:
         assert(sym != nil)
         if sym.typ == nil: invalidPragma(c, it)
@@ -1000,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wByRef:
         noVal(c, it)
         if sym == nil or sym.typ == nil:
-          if processOption(c, it): localError(c.config, it.info, "option expected")
+          processOption(c, it, c.config.options)
         else:
           incl(sym.typ.flags, tfByRef)
       of wByCopy:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 1f5ab7a13..ba87838db 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1453,11 +1453,12 @@ proc `$`*(n: PNode): string = n.renderTree
 
 proc renderModule*(n: PNode, infile, outfile: string,
                    renderFlags: TRenderFlags = {};
-                   fid = FileIndex(-1)) =
+                   fid = FileIndex(-1);
+                   conf: ConfigRef = nil) =
   var
     f: File
     g: TSrcGen
-  initSrcGen(g, renderFlags, newPartialConfigRef())
+  initSrcGen(g, renderFlags, conf)
   g.fid = fid
   for i in countup(0, sonsLen(n) - 1):
     gsub(g, n.sons[i])
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 439ef8fca..292238dc9 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1486,10 +1486,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     s.ast = n
     #s.scope = c.currentScope
 
+  s.options = c.config.options
+
   # before compiling the proc body, set as current the scope
   # where the proc was declared
   let oldScope = c.currentScope
-  let oldOptions = c.config.options
   #c.currentScope = s.scope
   pushOwner(c, s)
   openScope(c)
@@ -1558,6 +1559,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     addParams(c, proto.typ.n, proto.kind)
     proto.info = s.info       # more accurate line information
     s.typ = proto.typ
+    proto.options = s.options
     s = proto
     n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos]
     n.sons[paramsPos] = proto.ast.sons[paramsPos]
@@ -1569,8 +1571,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     proto.ast = n             # needed for code generation
     popOwner(c)
     pushOwner(c, s)
-  s.options = c.config.options
-  c.config.options = oldOptions
 
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if s.name.s[0] in {'.', '('}:
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/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..7ad243150 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
@@ -690,8 +692,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 +702,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 +714,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 +724,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 +789,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 +848,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 +898,40 @@ 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"
diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim
index 386eddfde..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,15 +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)
-  let tree = parseFile(fileIdx, newIdentCache(), conf)
-  let outfile = changeFileExt(infile, ".pretty.nim")
-  renderModule(tree, infile, outfile, {}, fileIdx)
+  conf.outFile = outfile
+  when defined(nimpretty2):
+    discard parseFile(fileIdx, newIdentCache(), conf)
+  else:
+    let tree = parseFile(fileIdx, newIdentCache(), conf)
+    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
@@ -58,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/nimpretty/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg
new file mode 100644
index 000000000..5fafa6d2a
--- /dev/null
+++ b/nimpretty/nimpretty.nim.cfg
@@ -0,0 +1,2 @@
+--define: nimpretty
+--define: nimpretty2
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..9f2141fbb
--- /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/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/overflw/toverflw.nim b/tests/overflw/toverflw.nim
index 771a43303..20bc56a53 100644
--- a/tests/overflw/toverflw.nim
+++ b/tests/overflw/toverflw.nim
@@ -1,21 +1,84 @@
 discard """
   file: "toverflw.nim"
-  output: "the computation overflowed"
+  output: "ok"
+  cmd: "nim $target -d:release $options $file"
+
 """
 # Tests nim's ability to detect overflows
 
 {.push overflowChecks: on.}
 
 var
-  a, b: int
-a = high(int)
-b = -2
+  a = high(int)
+  b = -2
+  overflowDetected = false
+
 try:
   writeLine(stdout, b - a)
 except OverflowError:
-  writeLine(stdout, "the computation overflowed")
+  overflowDetected = true
 
 {.pop.} # overflow check
-#OUT the computation overflowed
+
+doAssert(overflowDetected)
+
+block: # Overflow checks in a proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks in a forward declared proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  proc foo()
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks doesn't affect fwd declaration
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo()
+  {.pop.}
+
+  proc foo() =
+    let c = b - a
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(not overflowDetected)
 
 
+echo "ok"
diff --git a/tests/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)