summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-05-06 01:16:36 +0200
committerAraq <rumpf_a@web.de>2012-05-06 01:16:36 +0200
commitc323ec0155cc426e604e0c53a6baf00b17e439d7 (patch)
tree1f585f7bd06fd7b25b12e1d7329a33d093abaa4f
parentc3770ebd061649717d50bc74f718427af5be0ed5 (diff)
downloadNim-c323ec0155cc426e604e0c53a6baf00b17e439d7.tar.gz
added system.getStackTrace; docgen refactoring (incomplete)
-rwxr-xr-xcompiler/docgen.nim320
-rwxr-xr-xcompiler/lexer.nim2
-rwxr-xr-xcompiler/msgs.nim2
-rwxr-xr-xcompiler/nimrod.cfg2
-rwxr-xr-xcompiler/nimrod.ini12
-rwxr-xr-xcompiler/options.nim2
-rwxr-xr-xlib/system.nim3
-rwxr-xr-xlib/system/excpt.nim7
-rwxr-xr-xpackages/docutils/highlite.nim (renamed from compiler/highlite.nim)68
-rwxr-xr-xpackages/docutils/rst.nim (renamed from compiler/rst.nim)457
-rw-r--r--packages/docutils/rstast.nim288
-rw-r--r--packages/docutils/rstgen.nim87
-rwxr-xr-xweb/news.txt3
13 files changed, 744 insertions, 509 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 2bcb397c3..50abcfc4e 100755
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -13,11 +13,14 @@
 
 import 
   ast, astalgo, strutils, hashes, options, nversion, msgs, os, ropes, idents, 
-  wordrecg, math, syntaxes, renderer, lexer, rst, times, highlite, importer
+  wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, 
+  importer
 
 proc CommandDoc*()
 proc CommandRst2Html*()
 proc CommandRst2TeX*()
+
+#proc CommandBuildIndex*()
 # implementation
 
 type 
@@ -29,9 +32,9 @@ type
   TMetaEnum = enum 
     metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
   TDocumentor {.final.} = object # contains a module's documentation
+    target: TOutputTarget
     options: TRstParseOptions
     filename*: string         # filename of the source file; without extension
-    basedir*: string          # base directory (where to put the documentation)
     modDesc*: PRope           # module description
     id*: int                  # for generating IDs
     splitAfter*: int          # split too long entries in the TOC
@@ -40,13 +43,10 @@ type
     toc*, section*: TSections
     indexFile*, theIndex*: PRstNode
     indexValFilename*: string
-    indent*, verbatim*: int   # for code generation
     meta*: array[TMetaEnum, PRope]
 
   PDoc = ref TDocumentor
 
-var splitter: string = "<wbr />"
-
 proc findIndexNode(n: PRstNode): PRstNode = 
   if n == nil: 
     result = nil
@@ -59,10 +59,31 @@ proc findIndexNode(n: PRstNode): PRstNode =
       result = result.sons[0]
   else: 
     result = nil
-    for i in countup(0, rsonsLen(n) - 1): 
+    for i in countup(0, len(n) - 1): 
       result = findIndexNode(n.sons[i])
       if result != nil: return 
   
+proc compilerMsgHandler(filename: string, line, col: int,
+                        msgKind: rst.TMsgKind, arg: string) {.procvar.} =
+  # translate msg kind:
+  var k: msgs.TMsgKind
+  case msgKind
+  of meCannotOpenFile: k = errCannotOpenFile
+  of meExpected: k = errXExpected
+  of meGridTableNotImplemented: k = errGridTableNotImplemented
+  of meNewSectionExpected: k = errNewSectionExpected
+  of meGeneralParseError: k = errGeneralParseError
+  of meInvalidDirective: k = errInvalidDirectiveX
+  of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
+  of mwUnknownSubstitution: k = warnUnknownSubstitutionX
+  GlobalError(newLineInfo(filename, line, col), k, arg)
+  
+proc parseRst(text, filename: string,
+              line, column: int, hasToc: var bool,
+              rstOptions: TRstParseOptions): PRstNode =
+  result = rstParse(text, filename, line, column, hasToc, rstOptions,
+                    options.FindFile, compilerMsgHandler)
+  
 proc initIndexFile(d: PDoc) = 
   var 
     h: PRstNode
@@ -71,7 +92,7 @@ proc initIndexFile(d: PDoc) =
   gIndexFile = addFileExt(gIndexFile, "txt")
   d.indexValFilename = changeFileExt(extractFilename(d.filename), HtmlExt)
   if ExistsFile(gIndexFile): 
-    d.indexFile = rstParse(readFile(gIndexFile), gIndexFile, 0, 1, 
+    d.indexFile = parseRst(readFile(gIndexFile), gIndexFile, 0, 1, 
                            dummyHasToc, {roSupportRawDirective})
     d.theIndex = findIndexNode(d.indexFile)
     if (d.theIndex == nil) or (d.theIndex.kind != rnDefList): 
@@ -81,17 +102,19 @@ proc initIndexFile(d: PDoc) =
     d.indexFile = newRstNode(rnInner)
     h = newRstNode(rnOverline)
     h.level = 1
-    addSon(h, newRstNode(rnLeaf, "Index"))
-    addSon(d.indexFile, h)
+    add(h, newRstNode(rnLeaf, "Index"))
+    add(d.indexFile, h)
     h = newRstNode(rnIndex)
-    addSon(h, nil)            # no argument
-    addSon(h, nil)            # no options
+    add(h, nil)            # no argument
+    add(h, nil)            # no options
     d.theIndex = newRstNode(rnDefList)
-    addSon(h, d.theIndex)
-    addSon(d.indexFile, h)
+    add(h, d.theIndex)
+    add(d.indexFile, h)
 
 proc newDocumentor(filename: string): PDoc = 
   new(result)
+  if gCmd != cmdRst2Tex: result.target = outHtml
+  else: result.target = outLatex
   result.tocPart = @[]
   result.filename = filename
   result.id = 100
@@ -159,72 +182,7 @@ proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string],
       if (frmt[i] != '$'): inc(i)
       else: break 
     if i - 1 >= start: app(result, substr(frmt, start, i - 1))
-  
-proc addXmlChar(dest: var string, c: Char) = 
-  case c
-  of '&': add(dest, "&amp;")
-  of '<': add(dest, "&lt;")
-  of '>': add(dest, "&gt;")
-  of '\"': add(dest, "&quot;")
-  else: add(dest, c)
-  
-proc addRtfChar(dest: var string, c: Char) = 
-  case c
-  of '{': add(dest, "\\{")
-  of '}': add(dest, "\\}")
-  of '\\': add(dest, "\\\\")
-  else: add(dest, c)
-  
-proc addTexChar(dest: var string, c: Char) = 
-  case c
-  of '_': add(dest, "\\_")
-  of '{': add(dest, "\\symbol{123}")
-  of '}': add(dest, "\\symbol{125}")
-  of '[': add(dest, "\\symbol{91}")
-  of ']': add(dest, "\\symbol{93}")
-  of '\\': add(dest, "\\symbol{92}")
-  of '$': add(dest, "\\$")
-  of '&': add(dest, "\\&")
-  of '#': add(dest, "\\#")
-  of '%': add(dest, "\\%")
-  of '~': add(dest, "\\symbol{126}")
-  of '@': add(dest, "\\symbol{64}")
-  of '^': add(dest, "\\symbol{94}")
-  of '`': add(dest, "\\symbol{96}")
-  else: add(dest, c)
-  
-proc escChar(dest: var string, c: Char) = 
-  if gCmd != cmdRst2Tex: addXmlChar(dest, c)
-  else: addTexChar(dest, c)
-  
-proc nextSplitPoint(s: string, start: int): int = 
-  result = start
-  while result < len(s) + 0: 
-    case s[result]
-    of '_': return 
-    of 'a'..'z': 
-      if result + 1 < len(s) + 0: 
-        if s[result + 1] in {'A'..'Z'}: return 
-    else: nil
-    inc(result)
-  dec(result)                 # last valid index
-  
-proc esc(s: string, splitAfter: int = - 1): string = 
-  result = ""
-  if splitAfter >= 0: 
-    var partLen = 0
-    var j = 0
-    while j < len(s): 
-      var k = nextSplitPoint(s, j)
-      if (splitter != " ") or (partLen + k - j + 1 > splitAfter): 
-        partLen = 0
-        add(result, splitter)
-      for i in countup(j, k): escChar(result, s[i])
-      inc(partLen, k - j + 1)
-      j = k + 1
-  else: 
-    for i in countup(0, len(s) + 0 - 1): escChar(result, s[i])
-  
+
 proc disp(xml, tex: string): string = 
   if gCmd != cmdRst2Tex: result = xml
   else: result = tex
@@ -241,17 +199,17 @@ proc renderRstToOut(d: PDoc, n: PRstNode): PRope
 
 proc renderAux(d: PDoc, n: PRstNode, outer: string = "$1"): PRope = 
   result = nil
-  for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
+  for i in countup(0, len(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
   result = ropef(outer, [result])
 
 proc setIndexForSourceTerm(d: PDoc, name: PRstNode, id: int) = 
   if d.theIndex == nil: return 
   var h = newRstNode(rnHyperlink)
   var a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $id)
-  addSon(h, a)
-  addSon(h, a)
+  add(h, a)
+  add(h, a)
   a = newRstNode(rnIdx)
-  addSon(a, name)
+  add(a, name)
   setIndexPair(d.theIndex, a, h)
 
 proc renderIndexTerm(d: PDoc, n: PRstNode): PRope = 
@@ -260,14 +218,14 @@ proc renderIndexTerm(d: PDoc, n: PRstNode): PRope =
                  [toRope(d.id), renderAux(d, n)])
   var h = newRstNode(rnHyperlink)
   var a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $d.id)
-  addSon(h, a)
-  addSon(h, a)
+  add(h, a)
+  add(h, a)
   setIndexPair(d.theIndex, n, h)
 
 proc genComment(d: PDoc, n: PNode): PRope = 
   var dummyHasToc: bool
   if n.comment != nil and startsWith(n.comment, "##"):
-    result = renderRstToOut(d, rstParse(n.comment, toFilename(n.info),
+    result = renderRstToOut(d, parseRst(n.comment, toFilename(n.info),
                                         toLineNumber(n.info), toColumn(n.info), 
                                         dummyHasToc, 
                                         d.options + {roSkipPounds}))
@@ -294,16 +252,16 @@ proc isVisible(n: PNode): bool =
   elif n.kind == nkPragmaExpr: 
     result = isVisible(n.sons[0])
   
-proc getName(n: PNode, splitAfter: int = - 1): string = 
+proc getName(d: PDoc, n: PNode, splitAfter: int = - 1): string = 
   case n.kind
-  of nkPostfix: result = getName(n.sons[1], splitAfter)
-  of nkPragmaExpr: result = getName(n.sons[0], splitAfter)
-  of nkSym: result = esc(n.sym.name.s, splitAfter)
-  of nkIdent: result = esc(n.ident.s, splitAfter)
+  of nkPostfix: result = getName(d, n.sons[1], splitAfter)
+  of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter)
+  of nkSym: result = esc(d.target, n.sym.name.s, splitAfter)
+  of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
   of nkAccQuoted: 
-    result = esc("`") 
-    for i in 0.. <n.len: result.add(getName(n[i], splitAfter))
-    result.add esc("`")
+    result = esc(d.target, "`") 
+    for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter))
+    result.add esc(d.target, "`")
   else:
     internalError(n.info, "getName()")
     result = ""
@@ -323,7 +281,7 @@ proc getRstName(n: PNode): PRstNode =
 
 proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = 
   if not isVisible(nameNode): return 
-  var name = toRope(getName(nameNode))
+  var name = toRope(getName(d, nameNode))
   var result: PRope = nil
   var literal = ""
   var kind = tkEof
@@ -338,28 +296,28 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
       break 
     of tkComment: 
       dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", 
-            [toRope(esc(literal))])
+            [toRope(esc(d.target, literal))])
     of tokKeywordLow..tokKeywordHigh: 
       dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", 
             [toRope(literal)])
     of tkOpr: 
       dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", 
-            [toRope(esc(literal))])
+            [toRope(esc(d.target, literal))])
     of tkStrLit..tkTripleStrLit: 
       dispA(result, "<span class=\"StringLit\">$1</span>", 
-            "\\spanStringLit{$1}", [toRope(esc(literal))])
+            "\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
     of tkCharLit: 
       dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", 
-            [toRope(esc(literal))])
+            [toRope(esc(d.target, literal))])
     of tkIntLit..tkInt64Lit: 
       dispA(result, "<span class=\"DecNumber\">$1</span>", 
-            "\\spanDecNumber{$1}", [toRope(esc(literal))])
+            "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
     of tkFloatLit..tkFloat64Lit: 
       dispA(result, "<span class=\"FloatNumber\">$1</span>", 
-            "\\spanFloatNumber{$1}", [toRope(esc(literal))])
+            "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
     of tkSymbol: 
       dispA(result, "<span class=\"Identifier\">$1</span>", 
-            "\\spanIdentifier{$1}", [toRope(esc(literal))])
+            "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
     of tkInd, tkSad, tkDed, tkSpaces, tkInvalid: 
       app(result, literal)
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, 
@@ -368,19 +326,19 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
        tkAccent, tkColonColon, 
        tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: 
       dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", 
-            [toRope(esc(literal))])
+            [toRope(esc(d.target, literal))])
   inc(d.id)
   app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), 
                                         ["name", "header", "desc", "itemID"], 
                                         [name, result, comm, toRope(d.id)]))
   app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), 
                                     ["name", "header", "desc", "itemID"], [
-      toRope(getName(nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
+      toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
   setIndexForSourceTerm(d, getRstName(nameNode), d.id)
 
 proc renderHeadline(d: PDoc, n: PRstNode): PRope = 
   result = nil
-  for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
+  for i in countup(0, len(n) - 1): app(result, renderRstToOut(d, n.sons[i]))
   var refname = toRope(rstnodeToRefname(n))
   if d.hasToc: 
     var length = len(d.tocPart)
@@ -400,7 +358,7 @@ proc renderHeadline(d: PDoc, n: PRstNode): PRope =
   
 proc renderOverline(d: PDoc, n: PRstNode): PRope = 
   var t: PRope = nil
-  for i in countup(0, rsonsLen(n) - 1): app(t, renderRstToOut(d, n.sons[i]))
+  for i in countup(0, len(n) - 1): app(t, renderRstToOut(d, n.sons[i]))
   result = nil
   if d.meta[metaTitle] == nil: 
     d.meta[metaTitle] = t
@@ -411,123 +369,7 @@ proc renderOverline(d: PDoc, n: PRstNode): PRope =
                    "\\rstov$4{$3}\\label{$2}$n", [toRope(n.level), 
         toRope(rstnodeToRefname(n)), t, toRope($chr(n.level - 1 + ord('A')))])
   
-proc renderRstToRst(d: PDoc, n: PRstNode): PRope
-proc renderRstSons(d: PDoc, n: PRstNode): PRope = 
-  for i in countup(0, rsonsLen(n) - 1): 
-    app(result, renderRstToRst(d, n.sons[i]))
-  
-proc renderRstToRst(d: PDoc, n: PRstNode): PRope = 
-  # this is needed for the index generation; it may also be useful for
-  # debugging, but most code is already debugged...
-  const 
-    lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
-  result = nil
-  if n == nil: return 
-  var ind = toRope(repeatChar(d.indent))
-  case n.kind
-  of rnInner: 
-    result = renderRstSons(d, n)
-  of rnHeadline: 
-    result = renderRstSons(d, n)
-    var L = ropeLen(result)
-    result = ropef("$n$1$2$n$1$3", 
-                   [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))])
-  of rnOverline: 
-    result = renderRstSons(d, n)
-    var L = ropeLen(result)
-    result = ropef("$n$1$3$n$1$2$n$1$3", 
-                   [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))])
-  of rnTransition: 
-    result = ropef("$n$n$1$2$n$n", [ind, toRope(repeatChar(78-d.indent, '-'))])
-  of rnParagraph: 
-    result = renderRstSons(d, n)
-    result = ropef("$n$n$1$2", [ind, result])
-  of rnBulletItem: 
-    inc(d.indent, 2)
-    result = renderRstSons(d, n)
-    if result != nil: result = ropef("$n$1* $2", [ind, result])
-    dec(d.indent, 2)
-  of rnEnumItem: 
-    inc(d.indent, 4)
-    result = renderRstSons(d, n)
-    if result != nil: result = ropef("$n$1(#) $2", [ind, result])
-    dec(d.indent, 4)
-  of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, 
-     rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: 
-    result = renderRstSons(d, n)
-  of rnDefName: 
-    result = renderRstSons(d, n)
-    result = ropef("$n$n$1$2", [ind, result])
-  of rnDefBody: 
-    inc(d.indent, 2)
-    result = renderRstSons(d, n)
-    if n.sons[0].kind != rnBulletList: result = ropef("$n$1  $2", [ind, result])
-    dec(d.indent, 2)
-  of rnField: 
-    result = renderRstToRst(d, n.sons[0])
-    var L = max(ropeLen(result) + 3, 30)
-    inc(d.indent, L)
-    result = ropef("$n$1:$2:$3$4", [ind, result, toRope(
-        repeatChar(L - ropeLen(result) - 2)), renderRstToRst(d, n.sons[1])])
-    dec(d.indent, L)
-  of rnLineBlockItem: 
-    result = renderRstSons(d, n)
-    result = ropef("$n$1| $2", [ind, result])
-  of rnBlockQuote: 
-    inc(d.indent, 2)
-    result = renderRstSons(d, n)
-    dec(d.indent, 2)
-  of rnRef: 
-    result = renderRstSons(d, n)
-    result = ropef("`$1`_", [result])
-  of rnHyperlink: 
-    result = ropef("`$1 <$2>`_", 
-                   [renderRstToRst(d, n.sons[0]), renderRstToRst(d, n.sons[1])])
-  of rnGeneralRole: 
-    result = renderRstToRst(d, n.sons[0])
-    result = ropef("`$1`:$2:", [result, renderRstToRst(d, n.sons[1])])
-  of rnSub: 
-    result = renderRstSons(d, n)
-    result = ropef("`$1`:sub:", [result])
-  of rnSup: 
-    result = renderRstSons(d, n)
-    result = ropef("`$1`:sup:", [result])
-  of rnIdx: 
-    result = renderRstSons(d, n)
-    result = ropef("`$1`:idx:", [result])
-  of rnEmphasis: 
-    result = renderRstSons(d, n)
-    result = ropef("*$1*", [result])
-  of rnStrongEmphasis: 
-    result = renderRstSons(d, n)
-    result = ropef("**$1**", [result])
-  of rnTripleEmphasis:
-    result = renderRstSons(d, n)
-    result = ropef("***$1***", [result])
-  of rnInterpretedText: 
-    result = renderRstSons(d, n)
-    result = ropef("`$1`", [result])
-  of rnInlineLiteral: 
-    inc(d.verbatim)
-    result = renderRstSons(d, n)
-    result = ropef("``$1``", [result])
-    dec(d.verbatim)
-  of rnSmiley:
-    result = toRope(n.text)
-  of rnLeaf: 
-    if (d.verbatim == 0) and (n.text == "\\"): 
-      result = toRope("\\\\") # XXX: escape more special characters!
-    else: 
-      result = toRope(n.text)
-  of rnIndex: 
-    inc(d.indent, 3)
-    if n.sons[2] != nil: result = renderRstSons(d, n.sons[2])
-    dec(d.indent, 3)
-    result = ropef("$n$n$1.. index::$n$2", [ind, result])
-  of rnContents: 
-    result = ropef("$n$n$1.. contents::", [ind])
-  else: rawMessage(errCannotRenderX, $n.kind)
-  
+
 proc renderTocEntry(d: PDoc, e: TTocEntry): PRope = 
   result = dispF(
     "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>$n", 
@@ -537,10 +379,10 @@ proc renderTocEntries(d: PDoc, j: var int, lvl: int): PRope =
   result = nil
   while j <= high(d.tocPart): 
     var a = abs(d.tocPart[j].n.level)
-    if (a == lvl): 
+    if a == lvl: 
       app(result, renderTocEntry(d, d.tocPart[j]))
       inc(j)
-    elif (a > lvl): 
+    elif a > lvl: 
       app(result, renderTocEntries(d, j, a))
     else: 
       break 
@@ -566,7 +408,7 @@ proc renderImage(d: PDoc, n: PRstNode): PRope =
   if options != nil: options = dispF("$1", "[$1]", [options])
   result = dispF("<img src=\"$1\"$2 />", "\\includegraphics$2{$1}", 
                  [toRope(getArgument(n)), options])
-  if rsonsLen(n) >= 3: app(result, renderRstToOut(d, n.sons[2]))
+  if len(n) >= 3: app(result, renderRstToOut(d, n.sons[2]))
   
 proc renderSmiley(d: PDoc, n: PRstNode): PRope =
   result = dispF(
@@ -596,13 +438,13 @@ proc renderCodeBlock(d: PDoc, n: PRstNode): PRope =
       case g.kind
       of gtEof: break 
       of gtNone, gtWhitespace: 
-        app(result, substr(m.text, g.start + 0, g.length + g.start - 1))
+        app(result, substr(m.text, g.start, g.length + g.start - 1))
       else: 
         dispA(result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
-            toRope(esc(substr(m.text, g.start + 0, g.length + g.start - 1))), 
-            toRope(tokenClassToStr[g.kind])])
+          toRope(esc(d.target, substr(m.text, g.start, g.length+g.start-1))),
+          toRope(tokenClassToStr[g.kind])])
     deinitGeneralTokenizer(g)
-  if result != nil: 
+  if result != nil:
     result = dispF("<pre>$1</pre>", "\\begin{rstpre}$n$1$n\\end{rstpre}$n", 
                    [result])
   
@@ -614,13 +456,13 @@ proc renderContainer(d: PDoc, n: PRstNode): PRope =
   
 proc texColumns(n: PRstNode): string = 
   result = ""
-  for i in countup(1, rsonsLen(n)): add(result, "|X")
+  for i in countup(1, len(n)): add(result, "|X")
   
 proc renderField(d: PDoc, n: PRstNode): PRope = 
   var b = false
   if gCmd == cmdRst2Tex: 
     var fieldname = addNodes(n.sons[0])
-    var fieldval = toRope(esc(strip(addNodes(n.sons[1]))))
+    var fieldval = toRope(esc(d.target, strip(addNodes(n.sons[1]))))
     if cmpIgnoreStyle(fieldname, "author") == 0: 
       if d.meta[metaAuthor] == nil: 
         d.meta[metaAuthor] = fieldval
@@ -657,7 +499,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode): PRope =
   of rnDefBody: result = renderAux(d, n, disp("<dd>$1</dd>\n", "$1\n"))
   of rnFieldList: 
     result = nil
-    for i in countup(0, rsonsLen(n) - 1): 
+    for i in countup(0, len(n) - 1): 
       app(result, renderRstToOut(d, n.sons[i]))
     if result != nil: 
       result = dispf(
@@ -705,9 +547,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode): PRope =
       "\\begin{table}\\begin{rsttab}{" &
         texColumns(n) & "|}$n\\hline$n$1\\end{rsttab}\\end{table}"))
   of rnTableRow: 
-    if rsonsLen(n) >= 1: 
+    if len(n) >= 1: 
       result = renderRstToOut(d, n.sons[0])
-      for i in countup(1, rsonsLen(n) - 1): 
+      for i in countup(1, len(n) - 1): 
         dispa(result, "$1", " & $1", [renderRstToOut(d, n.sons[i])])
       result = dispf("<tr>$1</tr>$n", "$1\\\\$n\\hline$n", [result])
     else: 
@@ -770,7 +612,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode): PRope =
       "<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>", 
       "\\texttt{$1}"))
   of rnSmiley: result = renderSmiley(d, n)
-  of rnLeaf: result = toRope(esc(n.text))
+  of rnLeaf: result = toRope(esc(d.target, n.text))
   of rnContents: d.hasToc = true
   of rnTitle: d.meta[metaTitle] = renderRstToOut(d, n.sons[0])
   
@@ -863,7 +705,9 @@ proc genOutFile(d: PDoc): PRope =
 proc generateIndex(d: PDoc) = 
   if d.theIndex != nil: 
     sortIndex(d.theIndex)
-    writeRope(renderRstToRst(d, d.indexFile), gIndexFile)
+    var content = newStringOfCap(2_000_000)
+    renderRstToRst(d.indexFile, content)
+    writeFile(gIndexFile, content)
 
 proc writeOutput(d: PDoc, filename, outExt: string) = 
   var content = genOutFile(d)
@@ -886,7 +730,7 @@ proc CommandRstAux(filename, outExt: string) =
   var filen = addFileExt(filename, "txt")
   var d = newDocumentor(filen)
   initIndexFile(d)
-  var rst = rstParse(readFile(filen), filen, 0, 1, d.hasToc,
+  var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
                      {roSupportRawDirective})
   d.modDesc = renderRstToOut(d, rst)
   writeOutput(d, filename, outExt)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 496f7d2f1..a17871e3a 100755
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -27,6 +27,8 @@ const
   OpChars*: TCharSet = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', 
     '|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
 
+# don't forget to update the 'highlite' module if these charsets should change
+
 type 
   TTokType* = enum 
     tkInvalid, tkEof,         # order is important here!
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a0058ebe3..17cfeae4d 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -663,7 +663,7 @@ proc InternalError*(errMsg: string) =
   rawMessage(errInternal, errMsg)
 
 template AssertNotNil*(e: expr): expr =
-  if(e == nil): InternalError($InstantiationInfo())
+  if e == nil: InternalError($InstantiationInfo())
   e
 
 template InternalAssert*(e: bool): stmt =
diff --git a/compiler/nimrod.cfg b/compiler/nimrod.cfg
index 7d8d4d94f..f47143527 100755
--- a/compiler/nimrod.cfg
+++ b/compiler/nimrod.cfg
@@ -4,6 +4,8 @@
 path="llvm"
 path="$projectPath/.."
 
+path="$nimrod/packages/docutils"
+
 @if llvm_gcc or gcc:
   # GCC, LLVM and Visual C++ have a problem to optimize some modules.
   # This is really strange.
diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini
index 9fd6f0ce6..da87d957d 100755
--- a/compiler/nimrod.ini
+++ b/compiler/nimrod.ini
@@ -33,6 +33,7 @@ Files: "doc/*.pdf"
 Files: "doc/*.ini"
 Start: "doc/overview.html"
 
+
 [Other]
 Files: "readme.txt;install.txt;contributors.txt"
 Files: "configure;makefile"
@@ -56,6 +57,9 @@ Files: "compiler/*.nim"
 Files: "build/empty.txt"
 Files: "bin/empty.txt"
 
+Files: "packages/docutils/*.nim"
+
+
 [Lib]
 Files: "lib/nimbase.h;lib/cycle.h"
 Files: "lib/*.nim"
@@ -72,6 +76,7 @@ Files: "lib/wrappers/cairo/*.nim"
 Files: "lib/wrappers/gtk/*.nim"
 Files: "lib/wrappers/lua/*.nim"
 Files: "lib/wrappers/opengl/*.nim"
+Files: "lib/wrappers/readline/*.nim"
 Files: "lib/wrappers/sdl/*.nim"
 Files: "lib/wrappers/x11/*.nim"
 Files: "lib/wrappers/zip/*.nim"
@@ -81,6 +86,7 @@ Files: "lib/windows/*.nim"
 Files: "lib/posix/*.nim"
 Files: "lib/ecmas/*.nim"
 
+
 [Other]
 Files: "examples/*.nim"
 Files: "examples/gtk/*.nim"
@@ -99,6 +105,7 @@ Files: "examples/*.txt"
 Files: "examples/*.cfg"
 Files: "examples/*.tmpl"
 
+
 [Windows]
 Files: "bin/nimrod.exe"
 Files: "bin/c2nim.exe"
@@ -112,21 +119,26 @@ Files: "start.bat"
 BinPath: r"bin;dist\mingw\bin;dist"
 InnoSetup: "Yes"
 
+
 [UnixBin]
 Files: "bin/nimrod"
 
+
 [Unix]
 InstallScript: "yes"
 UninstallScript: "yes"
 
+
 [InnoSetup]
 path = r"c:\programme\inno setup 5\iscc.exe"
 flags = "/Q"
 
+
 [C_Compiler]
 path = r""
 flags = "-w"
 
+
 [deb]
 buildDepends: "gcc (>= 4:4.3.2)"
 pkgDepends: "gcc (>= 4:4.3.2)"
diff --git a/compiler/options.nim b/compiler/options.nim
index 3a2352c7f..9d7b41180 100755
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -199,7 +199,7 @@ proc rawFindFile(f: string): string =
       return result.canonicalizePath
   result = ""
 
-proc FindFile*(f: string): string = 
+proc FindFile*(f: string): string {.procvar.} = 
   result = rawFindFile(f)
   if len(result) == 0: result = rawFindFile(toLower(f))
 
diff --git a/lib/system.nim b/lib/system.nim
index ae6e4dc9f..0b89cc5fe 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1897,6 +1897,9 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   proc writeStackTrace*()
     ## writes the current stack trace to ``stderr``. This is only works
     ## for debug builds.
+
+  proc getStackTrace*(): string
+    ## gets the current stack trace. This is only works for debug builds.
     
   {.push stack_trace: off.}
   when hostCPU == "avr":
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 2df7fe4ad..a523e4e04 100755
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -246,6 +246,13 @@ proc WriteStackTrace() =
   else:
     writeToStdErr("No stack traceback available\n")
 
+proc getStackTrace(): string =
+  when hasSomeStackTrace:
+    result = ""
+    rawWriteStackTrace(result)
+  else:
+    result = "No stack traceback available\n"
+
 when defined(endb):
   var
     dbgAborting: bool # whether the debugger wants to abort
diff --git a/compiler/highlite.nim b/packages/docutils/highlite.nim
index ff4b27086..21dd1543a 100755
--- a/compiler/highlite.nim
+++ b/packages/docutils/highlite.nim
@@ -1,18 +1,18 @@
 #
 #
-#           The Nimrod Compiler
+#            Nimrod's Runtime Library
 #        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-# Source highlighter for programming or markup languages.
-# Currently only few languages are supported, other languages may be added.
-# The interface supports one language nested in another.
+## Source highlighter for programming or markup languages.
+## Currently only few languages are supported, other languages may be added.
+## The interface supports one language nested in another.
 
-import 
-  hashes, options, msgs, strutils, platform, idents, lexbase, wordrecg, lexer
+import
+  strutils
 
 type 
   TTokenClass* = enum 
@@ -44,19 +44,15 @@ const
     "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink", 
     "Label", "Reference", "Other"]
 
-proc getSourceLanguage*(name: string): TSourceLanguage
-proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string)
-proc deinitGeneralTokenizer*(g: var TGeneralTokenizer)
-proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage)
-# implementation
+  nimrodKeywords = slurp("doc/keywords.txt").split
 
-proc getSourceLanguage(name: string): TSourceLanguage = 
+proc getSourceLanguage*(name: string): TSourceLanguage = 
   for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)): 
     if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: 
       return i
   result = langNone
 
-proc initGeneralTokenizer(g: var TGeneralTokenizer, buf: string) = 
+proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = 
   g.buf = cstring(buf)
   g.kind = low(TTokenClass)
   g.start = 0
@@ -66,16 +62,20 @@ proc initGeneralTokenizer(g: var TGeneralTokenizer, buf: string) =
   while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
   g.pos = pos
 
-proc deinitGeneralTokenizer(g: var TGeneralTokenizer) = 
+proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = 
   nil
 
 proc nimGetKeyword(id: string): TTokenClass = 
-  var i = getIdent(id)
-  if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
-      (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): 
-    result = gtKeyword
-  else: 
-    result = gtIdentifier
+  for k in nimrodKeywords:
+    if cmpIgnoreStyle(id, k) == 0: return gtKeyword
+  result = gtIdentifier
+  when false:
+    var i = getIdent(id)
+    if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
+        (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): 
+      result = gtKeyword
+    else: 
+      result = gtIdentifier
   
 proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int = 
   var pos = position
@@ -111,11 +111,16 @@ proc nimNumber(g: var TGeneralTokenizer, position: int): int =
     while g.buf[pos] in decChars: inc(pos)
   result = nimNumberPostfix(g, pos)
 
+const
+  OpChars  = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', 
+              '|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
+
 proc nimNextToken(g: var TGeneralTokenizer) = 
   const 
     hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
     octChars = {'0'..'7', '_'}
     binChars = {'0'..'1', '_'}
+    SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
   var pos = g.pos
   g.start = g.pos
   if g.state == gtStringLit: 
@@ -154,7 +159,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
       while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
     of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF': 
       var id = ""
-      while g.buf[pos] in lexer.SymChars + {'_'}: 
+      while g.buf[pos] in SymChars + {'_'}: 
         add(id, g.buf[pos])
         inc(pos)
       if (g.buf[pos] == '\"'): 
@@ -247,15 +252,15 @@ proc nimNextToken(g: var TGeneralTokenizer) =
     of '\0': 
       g.kind = gtEof
     else: 
-      if g.buf[pos] in lexer.OpChars: 
+      if g.buf[pos] in OpChars: 
         g.kind = gtOperator
-        while g.buf[pos] in lexer.OpChars: inc(pos)
+        while g.buf[pos] in OpChars: inc(pos)
       else: 
         inc(pos)
         g.kind = gtNone
   g.length = pos - g.pos
-  if (g.kind != gtEof) and (g.length <= 0): 
-    InternalError("nimNextToken: " & $(g.buf))
+  if g.kind != gtEof and g.length <= 0:
+    assert false, "nimNextToken: produced an empty token"
   g.pos = pos
 
 proc generalNumber(g: var TGeneralTokenizer, position: int): int = 
@@ -407,7 +412,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openarray[string],
       inc(pos)
       if hasPreprocessor in flags: 
         g.kind = gtPreprocessor
-        while g.buf[pos] in {' ', Tabulator}: inc(pos)
+        while g.buf[pos] in {' ', '\t'}: inc(pos)
         while g.buf[pos] in symChars: inc(pos)
       else: 
         g.kind = gtOperator
@@ -462,14 +467,15 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openarray[string],
     of '\0': 
       g.kind = gtEof
     else: 
-      if g.buf[pos] in lexer.OpChars: 
+      if g.buf[pos] in OpChars: 
         g.kind = gtOperator
-        while g.buf[pos] in lexer.OpChars: inc(pos)
+        while g.buf[pos] in OpChars: inc(pos)
       else:
         inc(pos)
         g.kind = gtNone
   g.length = pos - g.pos
-  if (g.kind != gtEof) and (g.length <= 0): InternalError("clikeNextToken")
+  if g.kind != gtEof and g.length <= 0:
+    assert false, "clikeNextToken: produced an empty token"
   g.pos = pos
 
 proc cNextToken(g: var TGeneralTokenizer) = 
@@ -520,12 +526,12 @@ proc javaNextToken(g: var TGeneralTokenizer) =
       "try", "void", "volatile", "while"]
   clikeNextToken(g, keywords, {})
 
-proc getNextToken(g: var TGeneralTokenizer, lang: TSourceLanguage) = 
+proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) = 
   case lang
+  of langNone: assert false
   of langNimrod: nimNextToken(g)
   of langCpp: cppNextToken(g)
   of langCsharp: csharpNextToken(g)
   of langC: cNextToken(g)
   of langJava: javaNextToken(g)
-  else: InternalError("getNextToken")
   
diff --git a/compiler/rst.nim b/packages/docutils/rst.nim
index 5cb9c3b6a..cd385ccac 100755
--- a/compiler/rst.nim
+++ b/packages/docutils/rst.nim
@@ -1,95 +1,61 @@
 #
 #
-#           The Nimrod Compiler
+#            Nimrod's Runtime Library
 #        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-# This module implements a *reStructuredText* parser. A large
-# subset is provided.
+## This module implements a `reStructuredText`:idx parser. A large
+## subset is implemented. Some features of the `markdown`:idx: wiki syntax are
+## also supported.
 
 import 
-  os, msgs, strutils, hashes, options
-
-type 
-  TRstNodeKind* = enum 
-    rnInner,                  # an inner node or a root
-    rnHeadline,               # a headline
-    rnOverline,               # an over- and underlined headline
-    rnTransition,             # a transition (the ------------- <hr> thingie)
-    rnParagraph,              # a paragraph
-    rnBulletList,             # a bullet list
-    rnBulletItem,             # a bullet item
-    rnEnumList,               # an enumerated list
-    rnEnumItem,               # an enumerated item
-    rnDefList,                # a definition list
-    rnDefItem,                # an item of a definition list consisting of ...
-    rnDefName,                # ... a name part ...
-    rnDefBody,                # ... and a body part ...
-    rnFieldList,              # a field list
-    rnField,                  # a field item
-    rnFieldName,              # consisting of a field name ...
-    rnFieldBody,              # ... and a field body
-    rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, 
-    rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock,
-    rnLineBlock,              # the | thingie
-    rnLineBlockItem,          # sons of the | thing
-    rnBlockQuote,             # text just indented
-    rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
-    rnLabel,                  # used for footnotes and other things
-    rnFootnote,               # a footnote
-    rnCitation,               # similar to footnote
-    rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive
-    rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock,
-    rnRawHtml, rnRawLatex,
-    rnContainer,              # ``container`` directive
-    rnIndex,                  # index directve:
-                              # .. index::
-                              #   key
-                              #     * `file#id <file#id>`_
-                              #     * `file#id <file#id>'_
-    rnSubstitutionDef,        # a definition of a substitution
-    rnGeneralRole,            # Inline markup:
-    rnSub, rnSup, rnIdx, 
-    rnEmphasis,               # "*"
-    rnStrongEmphasis,         # "**"
-    rnTripleEmphasis,         # "***"
-    rnInterpretedText,        # "`"
-    rnInlineLiteral,          # "``"
-    rnSubstitutionReferences, # "|"
-    rnSmiley,                 # some smiley
-    rnLeaf                    # a leaf; the node's text field contains the
-                              # leaf val
+  os, strutils, rstast
 
 type
   TRstParseOption* = enum     ## options for the RST parser 
     roSkipPounds,             ## skip ``#`` at line beginning (documentation
                               ## embedded in Nimrod comments)
     roSupportSmilies,         ## make the RST parser support smilies like ``:)``
-    roSupportRawDirective     ## support the ``raw`` directive (don't support
+    roSupportRawDirective,    ## support the ``raw`` directive (don't support
                               ## it for sandboxing)
+    roSupportMarkdown         ## support additional features of markdown
   
   TRstParseOptions* = set[TRstParseOption]
   
-  PRSTNode* = ref TRstNode
-  TRstNodeSeq* = seq[PRstNode]
-  TRSTNode*{.acyclic, final.} = object 
-    kind*: TRstNodeKind
-    text*: string             # valid for leafs in the AST; and the title of
-                              # the document or the section
-    level*: int               # valid for some node kinds
-    sons*: TRstNodeSeq        # the node's sons
+  TMsgClass* = enum
+    mcHint = "Hint", 
+    mcWarning = "Warning", 
+    mcError = "Error"
   
+  TMsgKind* = enum          ## the possible messages
+    meCannotOpenFile,
+    meExpected,
+    meGridTableNotImplemented,
+    meNewSectionExpected,
+    meGeneralParseError,
+    meInvalidDirective,
+    mwRedefinitionOfLabel,
+    mwUnknownSubstitution
+  
+  TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
+                       arg: string) ## what to do in case of an error
+  TFindFileHandler* = proc (filename: string): string
+
+const
+  messages: array [TMsgKind, string] = [
+    meCannotOpenFile: "cannot open '$1'", 
+    meExpected: "'$1' expected",
+    meGridTableNotImplemented: "grid table is not implemented",
+    meNewSectionExpected: "new section expected", 
+    meGeneralParseError: "general parse error",
+    meInvalidDirective: "invalid directive: '$1'",
+    mwRedefinitionOfLabel: "redefinition of label '$1'", 
+    mwUnknownSubstitution: "unknown substitution '$1'"
+  ]
 
-proc rstParse*(text, filename: string,
-               line, column: int, hasToc: var bool,
-               options: TRstParseOptions): PRstNode
-proc rsonsLen*(n: PRstNode): int
-proc newRstNode*(kind: TRstNodeKind): PRstNode
-proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode
-proc addSon*(father, son: PRstNode)
 proc rstnodeToRefname*(n: PRstNode): string
 proc addNodes*(n: PRstNode): string
 proc getFieldValue*(n: PRstNode, fieldname: string): string
@@ -281,46 +247,27 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq) =
     tokens[0].ival = len(tokens[0].symbol)
     tokens[0].kind = tkIndent
 
-proc addSon(father, son: PRstNode) = 
-  add(father.sons, son)
-
-proc addSonIfNotNil(father, son: PRstNode) = 
-  if son != nil: addSon(father, son)
-  
-proc rsonsLen(n: PRstNode): int = 
-  result = len(n.sons)
-
-proc newRstNode(kind: TRstNodeKind): PRstNode = 
-  new(result)
-  result.sons = @[]
-  result.kind = kind
-
-proc newRstNode(kind: TRstNodeKind, s: string): PRstNode = 
-  result = newRstNode(kind)
-  result.text = s
-
-proc lastSon*(n: PRstNode): PRstNode = 
-  result = n.sons[len(n.sons)-1]
-
-type 
+type
   TLevelMap = array[Char, int]
   TSubstitution{.final.} = object 
     key*: string
     value*: PRstNode
 
   TSharedState {.final.} = object 
-    options: TRstParseOptions    # parsing options
-    uLevel*, oLevel*: int        # counters for the section levels
-    subs*: seq[TSubstitution]    # substitutions
-    refs*: seq[TSubstitution]    # references
-    underlineToLevel*: TLevelMap # Saves for each possible title adornment
-                                 # character its level in the
-                                 # current document. 
-                                 # This is for single underline adornments.
-    overlineToLevel*: TLevelMap  # Saves for each possible title adornment 
-                                 # character its level in the current
-                                 # document. 
-                                 # This is for over-underline adornments.
+    options: TRstParseOptions   # parsing options
+    uLevel, oLevel: int         # counters for the section levels
+    subs: seq[TSubstitution]    # substitutions
+    refs: seq[TSubstitution]    # references
+    underlineToLevel: TLevelMap # Saves for each possible title adornment
+                                # character its level in the
+                                # current document. 
+                                # This is for single underline adornments.
+    overlineToLevel: TLevelMap  # Saves for each possible title adornment 
+                                # character its level in the current
+                                # document. 
+                                # This is for over-underline adornments.
+    msgHandler: TMsgHandler     # How to handle errors.
+    findFile: TFindFileHandler  # How to find files.
   
   PSharedState = ref TSharedState
   TRstParser = object of TObject
@@ -332,21 +279,57 @@ type
     line*, col*: int
     hasToc*: bool
 
+  EParseError* = object of EInvalidValue
+
+when false:
+  proc tokInfo(p: TRstParser, tok: TToken): TLineInfo = 
+    result = newLineInfo(p.filename, p.line + tok.line, p.col + tok.col)
+
+  proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = 
+    GlobalError(tokInfo(p, p.tok[p.idx]), msgKind, arg)
+
+  proc rstMessage(p: TRstParser, msgKind: TMsgKind) = 
+    GlobalError(tokInfo(p, p.tok[p.idx]), msgKind, p.tok[p.idx].symbol)
+
 
-proc newSharedState(options: TRstParseOptions): PSharedState = 
+proc whichMsgClass*(k: TMsgKind): TMsgClass =
+  ## returns which message class `k` belongs to.
+  case ($k)[1]
+  of 'e', 'E': result = mcError
+  of 'w', 'W': result = mcWarning
+  of 'h', 'H': result = mcHint
+  else: assert false, "msgkind does not fit naming scheme"
+  
+proc defaultMsgHandler(filename: string, line, col: int, msgkind: TMsgKind,
+                       arg: string) =
+  let mc = msgKind.whichMsgClass
+  let a = messages[msgKind] % arg
+  let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a]
+  if mc == mcError: raise newException(EParseError, message)
+  else: Writeln(stdout, message)
+
+proc defaultFindFile(filename: string): string = 
+  if existsFile(filename): result = filename
+  else: result = ""
+
+proc newSharedState(options: TRstParseOptions,
+                    findFile: TFindFileHandler,
+                    msgHandler: TMsgHandler): PSharedState = 
   new(result)
   result.subs = @[]
   result.refs = @[]
   result.options = options
-
-proc tokInfo(p: TRstParser, tok: TToken): TLineInfo = 
-  result = newLineInfo(p.filename, p.line + tok.line, p.col + tok.col)
-
+  result.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler
+  result.findFile = if isNil(findFile): defaultFindFile else: findFile
+  
 proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = 
-  GlobalError(tokInfo(p, p.tok[p.idx]), msgKind, arg)
+  p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, 
+                             p.col + p.tok[p.idx].col, msgKind, arg)
 
 proc rstMessage(p: TRstParser, msgKind: TMsgKind) = 
-  GlobalError(tokInfo(p, p.tok[p.idx]), msgKind, p.tok[p.idx].symbol)
+  p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, 
+                             p.col + p.tok[p.idx].col, msgKind, 
+                             p.tok[p.idx].symbol)
 
 proc currInd(p: TRstParser): int = 
   result = p.indentStack[high(p.indentStack)]
@@ -371,7 +354,7 @@ proc addNodesAux(n: PRstNode, result: var string) =
   if n.kind == rnLeaf: 
     add(result, n.text)
   else: 
-    for i in countup(0, rsonsLen(n) - 1): addNodesAux(n.sons[i], result)
+    for i in countup(0, len(n) - 1): addNodesAux(n.sons[i], result)
   
 proc addNodes(n: PRstNode): string = 
   result = ""
@@ -400,7 +383,7 @@ proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) =
       else: 
         if (len(r) > 0): b = true
   else: 
-    for i in countup(0, rsonsLen(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b)
+    for i in countup(0, len(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b)
   
 proc rstnodeToRefname(n: PRstNode): string = 
   result = ""
@@ -433,7 +416,7 @@ proc setRef(p: var TRstParser, key: string, value: PRstNode) =
   for i in countup(0, length - 1): 
     if key == p.s.refs[i].key:
       if p.s.refs[i].value.addNodes != value.addNodes:
-        rstMessage(p, warnRedefinitionOfLabel, key)
+        rstMessage(p, mwRedefinitionOfLabel, key)
 
       p.s.refs[i].value = value
       return 
@@ -456,7 +439,7 @@ proc cmpNodes(a, b: PRstNode): int =
 proc sortIndex(a: PRstNode) = 
   # we use shellsort here; fast and simple
   assert(a.kind == rnDefList)
-  var N = rsonsLen(a)
+  var N = len(a)
   var h = 1
   while true: 
     h = 3 * h + 1
@@ -478,14 +461,14 @@ proc eqRstNodes(a, b: PRstNode): bool =
   if a.kind == rnLeaf: 
     result = a.text == b.text
   else: 
-    if rsonsLen(a) != rsonsLen(b): return 
-    for i in countup(0, rsonsLen(a) - 1): 
+    if len(a) != len(b): return 
+    for i in countup(0, len(a) - 1): 
       if not eqRstNodes(a.sons[i], b.sons[i]): return 
     result = true
 
 proc matchesHyperlink(h: PRstNode, filename: string): bool = 
   if h.kind == rnInner:       # this may happen in broken indexes!
-    assert(rsonsLen(h) == 1)
+    assert(len(h) == 1)
     result = matchesHyperlink(h.sons[0], filename)
   elif h.kind == rnHyperlink: 
     var s = addNodes(h.sons[1])
@@ -498,14 +481,14 @@ proc clearIndex(index: PRstNode, filename: string) =
   var 
     lastItem: int
   assert(index.kind == rnDefList)
-  for i in countup(0, rsonsLen(index) - 1): 
+  for i in countup(0, len(index) - 1): 
     assert(index.sons[i].sons[1].kind == rnDefBody)
     var val = index.sons[i].sons[1].sons[0]
     if val.kind == rnInner: val = val.sons[0]
     if val.kind == rnBulletList: 
-      var items = rsonsLen(val)
+      var items = len(val)
       lastItem = - 1          # save the last valid item index
-      for j in countup(0, rsonsLen(val) - 1): 
+      for j in countup(0, len(val) - 1): 
         if val.sons[j] == nil: 
           dec(items)
         elif matchesHyperlink(val.sons[j].sons[0], filename): 
@@ -520,7 +503,7 @@ proc clearIndex(index: PRstNode, filename: string) =
     elif matchesHyperlink(val, filename): 
       index.sons[i] = nil
   var k = 0
-  for i in countup(0, rsonsLen(index) - 1): 
+  for i in countup(0, len(index) - 1): 
     if index.sons[i] != nil: 
       if k != i: index.sons[k] = index.sons[i]
       inc(k)
@@ -531,28 +514,28 @@ proc setIndexPair(index, key, val: PRstNode) =
   assert(index.kind == rnDefList)
   assert(key.kind != rnDefName)
   a = newRstNode(rnDefName)
-  addSon(a, key)
-  for i in countup(0, rsonsLen(index) - 1): 
+  add(a, key)
+  for i in countup(0, len(index) - 1): 
     if eqRstNodes(index.sons[i].sons[0], a): 
       assert(index.sons[i].sons[1].kind == rnDefBody)
       e = index.sons[i].sons[1].sons[0]
       if e.kind != rnBulletList: 
         e = newRstNode(rnBulletList)
         b = newRstNode(rnBulletItem)
-        addSon(b, index.sons[i].sons[1].sons[0])
-        addSon(e, b)
+        add(b, index.sons[i].sons[1].sons[0])
+        add(e, b)
         index.sons[i].sons[1].sons[0] = e
       b = newRstNode(rnBulletItem)
-      addSon(b, val)
-      addSon(e, b)
+      add(b, val)
+      add(e, b)
       return                  # key already exists
   e = newRstNode(rnDefItem)
   assert(val.kind != rnDefBody)
   b = newRstNode(rnDefBody)
-  addSon(b, val)
-  addSon(e, a)
-  addSon(e, b)
-  addSon(index, e)
+  add(b, val)
+  add(e, a)
+  add(e, b)
+  add(index, e)
 
 proc newLeaf(p: var TRstParser): PRstNode = 
   result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
@@ -562,15 +545,15 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
   while true: 
     case p.tok[p.idx].kind
     of tkWord, tkOther, tkWhite: 
-      addSon(res, newLeaf(p))
+      add(res, newLeaf(p))
     of tkPunct: 
       if p.tok[p.idx].symbol == endStr: 
         inc(p.idx)
         break 
       else: 
-        addSon(res, newLeaf(p))
+        add(res, newLeaf(p))
     else: 
-      rstMessage(p, errXexpected, endStr)
+      rstMessage(p, meExpected, endStr)
       break 
     inc(p.idx)
   result = res
@@ -578,12 +561,12 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
 proc untilEol(p: var TRstParser): PRstNode = 
   result = newRstNode(rnInner)
   while not (p.tok[p.idx].kind in {tkIndent, tkEof}): 
-    addSon(result, newLeaf(p))
+    add(result, newLeaf(p))
     inc(p.idx)
 
 proc expect(p: var TRstParser, tok: string) = 
   if p.tok[p.idx].symbol == tok: inc(p.idx)
-  else: rstMessage(p, errXexpected, tok)
+  else: rstMessage(p, meExpected, tok)
   
 proc isInlineMarkupEnd(p: TRstParser, markup: string): bool = 
   result = p.tok[p.idx].symbol == markup
@@ -676,13 +659,13 @@ proc match(p: TRstParser, start: int, expr: string): bool =
   
 proc fixupEmbeddedRef(n, a, b: PRstNode) = 
   var sep = - 1
-  for i in countdown(rsonsLen(n) - 2, 0): 
+  for i in countdown(len(n) - 2, 0): 
     if n.sons[i].text == "<": 
       sep = i
       break 
   var incr = if (sep > 0) and (n.sons[sep - 1].text[0] == ' '): 2 else: 1
-  for i in countup(0, sep - incr): addSon(a, n.sons[i])
-  for i in countup(sep + 1, rsonsLen(n) - 2): addSon(b, n.sons[i])
+  for i in countup(0, sep - incr): add(a, n.sons[i])
+  for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
   
 proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = 
   result = n
@@ -692,19 +675,19 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
       var a = newRstNode(rnInner)
       var b = newRstNode(rnInner)
       fixupEmbeddedRef(n, a, b)
-      if rsonsLen(a) == 0: 
+      if len(a) == 0: 
         result = newRstNode(rnStandaloneHyperlink)
-        addSon(result, b)
+        add(result, b)
       else: 
         result = newRstNode(rnHyperlink)
-        addSon(result, a)
-        addSon(result, b)
+        add(result, a)
+        add(result, b)
         setRef(p, rstnodeToRefname(a), b)
     elif n.kind == rnInterpretedText: 
       n.kind = rnRef
     else: 
       result = newRstNode(rnRef)
-      addSon(result, n)
+      add(result, n)
   elif match(p, p.idx, ":w:"): 
     # a role:
     if p.tok[p.idx + 1].symbol == "idx": 
@@ -724,8 +707,8 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
     else: 
       result = newRstNode(rnGeneralRole)
       n.kind = rnInner
-      addSon(result, n)
-      addSon(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
+      add(result, n)
+      add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
     inc(p.idx, 3)
 
 proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
@@ -761,27 +744,27 @@ proc parseURL(p: var TRstParser, father: PRstNode) =
         if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
           break
       else: break 
-      addSon(n, newLeaf(p))
+      add(n, newLeaf(p))
       inc(p.idx)
-    addSon(father, n)
+    add(father, n)
   else:
     var n = newLeaf(p)
     inc(p.idx)
     if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
-    addSon(father, n)
+    add(father, n)
   
 proc parseBackslash(p: var TRstParser, father: PRstNode) = 
   assert(p.tok[p.idx].kind == tkPunct)
   if p.tok[p.idx].symbol == "\\\\": 
-    addSon(father, newRstNode(rnLeaf, "\\"))
+    add(father, newRstNode(rnLeaf, "\\"))
     inc(p.idx)
   elif p.tok[p.idx].symbol == "\\": 
     # XXX: Unicode?
     inc(p.idx)
-    if p.tok[p.idx].kind != tkWhite: addSon(father, newLeaf(p))
+    if p.tok[p.idx].kind != tkWhite: add(father, newLeaf(p))
     inc(p.idx)
   else: 
-    addSon(father, newLeaf(p))
+    add(father, newLeaf(p))
     inc(p.idx)
 
 when false:
@@ -795,18 +778,18 @@ when false:
           if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
             break
         else: break 
-        addSon(n, newLeaf(p))
+        add(n, newLeaf(p))
         inc(p.idx)
-      addSon(father, n)
+      add(father, n)
     elif not verbatim and roSupportSmilies in p.shared.options:
       let n = parseSmiley(p)
       if s != nil:
-        addSon(father, n)
+        add(father, n)
     else:
       var n = newLeaf(p)
       inc(p.idx)
       if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
-      addSon(father, n)
+      add(father, n)
 
 proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, 
                 interpretBackslash: bool) = 
@@ -819,21 +802,21 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
       elif interpretBackslash: 
         parseBackslash(p, father)
       else: 
-        addSon(father, newLeaf(p))
+        add(father, newLeaf(p))
         inc(p.idx)
     of tkAdornment, tkWord, tkOther: 
-      addSon(father, newLeaf(p))
+      add(father, newLeaf(p))
       inc(p.idx)
     of tkIndent: 
-      addSon(father, newRstNode(rnLeaf, " "))
+      add(father, newRstNode(rnLeaf, " "))
       inc(p.idx)
       if p.tok[p.idx].kind == tkIndent: 
-        rstMessage(p, errXExpected, postfix)
+        rstMessage(p, meExpected, postfix)
         break 
     of tkWhite: 
-      addSon(father, newRstNode(rnLeaf, " "))
+      add(father, newRstNode(rnLeaf, " "))
       inc(p.idx)
-    else: rstMessage(p, errXExpected, postfix)
+    else: rstMessage(p, meExpected, postfix)
   
 proc parseInline(p: var TRstParser, father: PRstNode) = 
   case p.tok[p.idx].kind
@@ -842,54 +825,54 @@ proc parseInline(p: var TRstParser, father: PRstNode) =
       inc(p.idx)
       var n = newRstNode(rnTripleEmphasis)
       parseUntil(p, n, "***", true)
-      addSon(father, n)
+      add(father, n)
     elif isInlineMarkupStart(p, "**"): 
       inc(p.idx)
       var n = newRstNode(rnStrongEmphasis)
       parseUntil(p, n, "**", true)
-      addSon(father, n)
+      add(father, n)
     elif isInlineMarkupStart(p, "*"): 
       inc(p.idx)
       var n = newRstNode(rnEmphasis)
       parseUntil(p, n, "*", true)
-      addSon(father, n)
+      add(father, n)
     elif isInlineMarkupStart(p, "``"): 
       inc(p.idx)
       var n = newRstNode(rnInlineLiteral)
       parseUntil(p, n, "``", false)
-      addSon(father, n)
+      add(father, n)
     elif isInlineMarkupStart(p, "`"): 
       inc(p.idx)
       var n = newRstNode(rnInterpretedText)
       parseUntil(p, n, "`", true)
       n = parsePostfix(p, n)
-      addSon(father, n)
+      add(father, n)
     elif isInlineMarkupStart(p, "|"): 
       inc(p.idx)
       var n = newRstNode(rnSubstitutionReferences)
       parseUntil(p, n, "|", false)
-      addSon(father, n)
+      add(father, n)
     else:
       if roSupportSmilies in p.s.options:
         let n = parseSmiley(p)
         if n != nil:
-          addSon(father, n)
+          add(father, n)
           return
       parseBackslash(p, father)
   of tkWord:
     if roSupportSmilies in p.s.options:
       let n = parseSmiley(p)
       if n != nil:
-        addSon(father, n)
+        add(father, n)
         return
     parseURL(p, father)
   of tkAdornment, tkOther, tkWhite: 
     if roSupportSmilies in p.s.options:
       let n = parseSmiley(p)
       if n != nil:
-        addSon(father, n)
+        add(father, n)
         return
-    addSon(father, newLeaf(p))
+    add(father, newLeaf(p))
     inc(p.idx)
   else: nil
   
@@ -944,8 +927,7 @@ const
     "title"]
 
 proc getDirKind(s: string): TDirKind = 
-  var i: int
-  i = binaryStrSearch(DirIds, s)
+  let i = find(DirIds, s)
   if i >= 0: result = TDirKind(i)
   else: result = dkNone
   
@@ -970,8 +952,8 @@ proc parseField(p: var TRstParser): PRstNode =
       pushInd(p, indent)
       parseSection(p, fieldbody)
       popInd(p)
-  addSon(result, fieldname)
-  addSon(result, fieldbody)
+  add(result, fieldname)
+  add(result, fieldbody)
 
 proc parseFields(p: var TRstParser): PRstNode = 
   result = nil
@@ -982,7 +964,7 @@ proc parseFields(p: var TRstParser): PRstNode =
     result = newRstNode(rnFieldList)
     if not atStart: inc(p.idx)
     while true: 
-      addSon(result, parseField(p))
+      add(result, parseField(p))
       if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
           (p.tok[p.idx + 1].symbol == ":"): 
         inc(p.idx)
@@ -996,7 +978,7 @@ proc getFieldValue(n: PRstNode, fieldname: string): string =
     #InternalError("getFieldValue (2): " & $n.sons[1].kind)
     # We don't like internal errors here anymore as that would break the forum!
     return
-  for i in countup(0, rsonsLen(n.sons[1]) - 1): 
+  for i in countup(0, len(n.sons[1]) - 1): 
     var f = n.sons[1].sons[i]
     if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0: 
       result = addNodes(f.sons[1])
@@ -1032,7 +1014,7 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
     while not (p.tok[p.idx].kind in {tkIndent, tkEof}): 
       add(n.text, p.tok[p.idx].symbol)
       inc(p.idx)
-  addSon(result, n)
+  add(result, n)
 
 proc getLevel(map: var TLevelMap, lvl: var int, c: Char): int = 
   if map[c] == 0: 
@@ -1099,7 +1081,7 @@ proc whichSection(p: TRstParser): TRstNodeKind =
       result = rnEnumList
     elif match(p, p.idx, "+a+"): 
       result = rnGridTable
-      rstMessage(p, errGridTableNotImplemented)
+      rstMessage(p, meGridTableNotImplemented)
     elif isDefList(p): 
       result = rnDefList
     elif isOptionList(p): 
@@ -1123,7 +1105,7 @@ proc parseLineBlock(p: var TRstParser): PRstNode =
     while true: 
       var item = newRstNode(rnLineBlockItem)
       parseSection(p, item)
-      addSon(result, item)
+      add(result, item)
       if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
           (p.tok[p.idx + 1].symbol == "|") and
           (p.tok[p.idx + 2].kind == tkWhite): 
@@ -1143,9 +1125,9 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
         inc(p.idx)
         case whichSection(p)
         of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective: 
-          addSon(result, newRstNode(rnLeaf, " "))
+          add(result, newRstNode(rnLeaf, " "))
         of rnLineBlock: 
-          addSonIfNotNil(result, parseLineBlock(p))
+          addIfNotNil(result, parseLineBlock(p))
         else: break 
       else: 
         break 
@@ -1153,9 +1135,9 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
       if (p.tok[p.idx].symbol == "::") and
           (p.tok[p.idx + 1].kind == tkIndent) and
           (currInd(p) < p.tok[p.idx + 1].ival): 
-        addSon(result, newRstNode(rnLeaf, ":"))
+        add(result, newRstNode(rnLeaf, ":"))
         inc(p.idx)            # skip '::'
-        addSon(result, parseLiteralBlock(p))
+        add(result, parseLiteralBlock(p))
         break 
       else: 
         parseInline(p, result)
@@ -1222,7 +1204,7 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
       getColumns(p, cols)
       setlen(row, len(cols))
       if a != nil: 
-        for j in 0..rsonsLen(a)-1: a.sons[j].kind = rnTableHeaderCell
+        for j in 0..len(a)-1: a.sons[j].kind = rnTableHeaderCell
     if p.tok[p.idx].kind == tkEof: break 
     for j in countup(0, high(row)): row[j] = ""
     # the following while loop iterates over the lines a single cell may span:
@@ -1248,9 +1230,9 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
       q.filename = p.filename
       getTokens(row[j], false, q.tok)
       b = newRstNode(rnTableDataCell)
-      addSon(b, parseDoc(q))
-      addSon(a, b)
-    addSon(result, a)
+      add(b, parseDoc(q))
+      add(a, b)
+    add(result, a)
 
 proc parseTransition(p: var TRstParser): PRstNode = 
   result = newRstNode(rnTransition)
@@ -1267,7 +1249,7 @@ proc parseOverline(p: var TRstParser): PRstNode =
     if p.tok[p.idx].kind == tkIndent: 
       inc(p.idx)
       if p.tok[p.idx - 1].ival > currInd(p): 
-        addSon(result, newRstNode(rnLeaf, " "))
+        add(result, newRstNode(rnLeaf, " "))
       else: 
         break 
     else: 
@@ -1288,7 +1270,7 @@ proc parseBulletList(p: var TRstParser): PRstNode =
     while true: 
       var item = newRstNode(rnBulletItem)
       parseSection(p, item)
-      addSon(result, item)
+      add(result, item)
       if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
           (p.tok[p.idx + 1].symbol == bullet) and
           (p.tok[p.idx + 2].kind == tkWhite): 
@@ -1309,7 +1291,7 @@ proc parseOptionList(p: var TRstParser): PRstNode =
         if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1): 
           inc(p.idx)
           break 
-        addSon(a, newLeaf(p))
+        add(a, newLeaf(p))
         inc(p.idx)
       var j = tokenAfterNewline(p)
       if (j > 0) and (p.tok[j - 1].kind == tkIndent) and
@@ -1320,9 +1302,9 @@ proc parseOptionList(p: var TRstParser): PRstNode =
       else: 
         parseLine(p, b)
       if (p.tok[p.idx].kind == tkIndent): inc(p.idx)
-      addSon(c, a)
-      addSon(c, b)
-      addSon(result, c)
+      add(c, a)
+      add(c, b)
+      add(result, c)
     else: 
       break 
   
@@ -1345,9 +1327,9 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
         var b = newRstNode(rnDefBody)
         parseSection(p, b)
         var c = newRstNode(rnDefItem)
-        addSon(c, a)
-        addSon(c, b)
-        addSon(result, c)
+        add(c, a)
+        add(c, b)
+        add(result, c)
         popInd(p)
       else: 
         p.idx = j
@@ -1360,7 +1342,7 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
           nil
         else: 
           break 
-    if rsonsLen(result) == 0: result = nil
+    if len(result) == 0: result = nil
   
 proc parseEnumList(p: var TRstParser): PRstNode = 
   const 
@@ -1381,7 +1363,7 @@ proc parseEnumList(p: var TRstParser): PRstNode =
       while true: 
         var item = newRstNode(rnEnumItem)
         parseSection(p, item)
-        addSon(result, item)
+        add(result, item)
         if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
             match(p, p.idx + 1, wildcards[w]): 
           inc(p.idx, wildpos[w] + 4)
@@ -1394,7 +1376,7 @@ proc parseEnumList(p: var TRstParser): PRstNode =
 
 proc sonKind(father: PRstNode, i: int): TRstNodeKind = 
   result = rnLeaf
-  if i < rsonsLen(father): result = father.sons[i].kind
+  if i < len(father): result = father.sons[i].kind
   
 proc parseSection(p: var TRstParser, result: PRstNode) = 
   while true: 
@@ -1407,7 +1389,7 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
         pushInd(p, p.tok[p.idx].ival)
         var a = newRstNode(rnBlockQuote)
         parseSection(p, a)
-        addSon(result, a)
+        add(result, a)
         popInd(p)
       else: 
         leave = true
@@ -1424,7 +1406,7 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
     of rnLineblock: a = parseLineBlock(p)
     of rnDirective: a = parseDotDot(p)
     of rnEnumList: a = parseEnumList(p)
-    of rnLeaf: rstMessage(p, errNewSectionExpected)
+    of rnLeaf: rstMessage(p, meNewSectionExpected)
     of rnParagraph: nil
     of rnDefList: a = parseDefinitionList(p)
     of rnFieldList: 
@@ -1441,19 +1423,19 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
     if a == nil and k != rnDirective: 
       a = newRstNode(rnParagraph)
       parseParagraph(p, a)
-    addSonIfNotNil(result, a)
+    addIfNotNil(result, a)
   if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: 
     result.sons[0].kind = rnInner
   
 proc parseSectionWrapper(p: var TRstParser): PRstNode = 
   result = newRstNode(rnInner)
   parseSection(p, result)
-  while (result.kind == rnInner) and (rsonsLen(result) == 1): 
+  while (result.kind == rnInner) and (len(result) == 1): 
     result = result.sons[0]
   
 proc parseDoc(p: var TRstParser): PRstNode = 
   result = parseSectionWrapper(p)
-  if p.tok[p.idx].kind != tkEof: rstMessage(p, errGeneralParseError)
+  if p.tok[p.idx].kind != tkEof: rstMessage(p, meGeneralParseError)
   
 type
   TDirFlag = enum 
@@ -1471,24 +1453,24 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
       while True: 
         case p.tok[p.idx].kind
         of tkWord, tkOther, tkPunct, tkAdornment: 
-          addSon(args, newLeaf(p))
+          add(args, newLeaf(p))
           inc(p.idx)
         else: break 
     elif argIsWord in flags:
       while p.tok[p.idx].kind == tkWhite: inc(p.idx)
       if p.tok[p.idx].kind == tkWord: 
-        addSon(args, newLeaf(p))
+        add(args, newLeaf(p))
         inc(p.idx)
       else:
         args = nil
     else: 
       parseLine(p, args)
-  addSon(result, args)
+  add(result, args)
   if hasOptions in flags: 
     if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival >= 3) and
         (p.tok[p.idx + 1].symbol == ":"): 
       options = parseFields(p)
-  addSon(result, options)
+  add(result, options)
   
 proc indFollows(p: TRstParser): bool = 
   result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
@@ -1500,9 +1482,9 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags,
     pushInd(p, p.tok[p.idx].ival)
     var content = contentParser(p)
     popInd(p)
-    addSon(result, content)
+    add(result, content)
   else: 
-    addSon(result, nil)
+    add(result, nil)
 
 proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode = 
   if indFollows(p): 
@@ -1530,14 +1512,14 @@ proc dirInclude(p: var TRstParser): PRstNode =
   result = nil
   var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil)
   var filename = strip(addNodes(n.sons[0]))
-  var path = findFile(filename)
+  var path = p.s.findFile(filename)
   if path == "": 
-    rstMessage(p, errCannotOpenFile, filename)
+    rstMessage(p, meCannotOpenFile, filename)
   else: 
     # XXX: error handling; recursive file inclusion!
     if getFieldValue(n, "literal") != "": 
       result = newRstNode(rnLiteralBlock)
-      addSon(result, newRstNode(rnLeaf, readFile(path)))
+      add(result, newRstNode(rnLeaf, readFile(path)))
     else: 
       var q: TRstParser
       initParser(q, p.s)
@@ -1552,17 +1534,17 @@ proc dirCodeBlock(p: var TRstParser): PRstNode =
   result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
   var filename = strip(getFieldValue(result, "file"))
   if filename != "": 
-    var path = findFile(filename)
-    if path == "": rstMessage(p, errCannotOpenFile, filename)
+    var path = p.s.findFile(filename)
+    if path == "": rstMessage(p, meCannotOpenFile, filename)
     var n = newRstNode(rnLiteralBlock)
-    addSon(n, newRstNode(rnLeaf, readFile(path)))
+    add(n, newRstNode(rnLeaf, readFile(path)))
     result.sons[2] = n
   result.kind = rnCodeBlock
 
 proc dirContainer(p: var TRstParser): PRstNode = 
   result = parseDirective(p, {hasArg}, parseSectionWrapper)
   assert(result.kind == rnDirective)
-  assert(rsonsLen(result) == 3)
+  assert(len(result) == 3)
   result.kind = rnContainer
 
 proc dirImage(p: var TRstParser): PRstNode = 
@@ -1590,16 +1572,16 @@ proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
                contentParser: TSectionParser) = 
   var filename = getFieldValue(result, "file")
   if filename.len > 0: 
-    var path = findFile(filename)
+    var path = p.s.findFile(filename)
     if path.len == 0: 
-      rstMessage(p, errCannotOpenFile, filename)
+      rstMessage(p, meCannotOpenFile, filename)
     else: 
       var f = readFile(path)
       result = newRstNode(kind)
-      addSon(result, newRstNode(rnLeaf, f))
+      add(result, newRstNode(rnLeaf, f))
   else:      
     result.kind = kind
-    addSon(result, parseDirBody(p, contentParser))
+    add(result, parseDirBody(p, contentParser))
 
 proc dirRaw(p: var TRstParser): PRstNode = 
   #
@@ -1617,7 +1599,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
     elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: 
       dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
     else:
-      rstMessage(p, errInvalidDirectiveX, result.sons[0].text)
+      rstMessage(p, meInvalidDirective, result.sons[0].text)
   else:
     dirRawAux(p, result, rnRaw, parseSectionWrapper)
 
@@ -1639,10 +1621,10 @@ proc parseDotDot(p: var TRstParser): PRstNode =
       if roSupportRawDirective in p.s.options:
         result = dirRaw(p)
       else:
-        rstMessage(p, errInvalidDirectiveX, d)
+        rstMessage(p, meInvalidDirective, d)
     of dkCodeblock: result = dirCodeBlock(p)
     of dkIndex: result = dirIndex(p)
-    else: rstMessage(p, errInvalidDirectiveX, d)
+    else: rstMessage(p, meInvalidDirective, d)
     popInd(p)
   elif match(p, p.idx, " _"): 
     # hyperlink target:
@@ -1665,7 +1647,7 @@ proc parseDotDot(p: var TRstParser): PRstNode =
       inc(p.idx)
       b = dirImage(p)
     else: 
-      rstMessage(p, errInvalidDirectiveX, p.tok[p.idx].symbol)
+      rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol)
     setSub(p, addNodes(a), b)
   elif match(p, p.idx, " ["): 
     # footnotes, citations
@@ -1689,27 +1671,28 @@ proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
       var key = addNodes(n)
       var e = getEnv(key)
       if e != "": result = newRstNode(rnLeaf, e)
-      else: rstMessage(p, warnUnknownSubstitutionX, key)
+      else: rstMessage(p, mwUnknownSubstitution, key)
   of rnRef: 
     var y = findRef(p, rstnodeToRefname(n))
     if y != nil: 
       result = newRstNode(rnHyperlink)
       n.kind = rnInner
-      addSon(result, n)
-      addSon(result, y)
+      add(result, n)
+      add(result, y)
   of rnLeaf: 
     nil
   of rnContents: 
     p.hasToc = true
   else: 
-    for i in countup(0, rsonsLen(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i])
+    for i in countup(0, len(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i])
   
-proc rstParse(text, filename: string,
-              line, column: int, hasToc: var bool,
-              options: TRstParseOptions): PRstNode =
+proc rstParse*(text, filename: string,
+               line, column: int, hasToc: var bool,
+               options: TRstParseOptions,
+               findFile: TFindFileHandler = nil,
+               msgHandler: TMsgHandler = nil): PRstNode =
   var p: TRstParser
-  if isNil(text): rawMessage(errCannotOpenFile, filename)
-  initParser(p, newSharedState(options))
+  initParser(p, newSharedState(options, findFile, msgHandler))
   p.filename = filename
   p.line = line
   p.col = column
diff --git a/packages/docutils/rstast.nim b/packages/docutils/rstast.nim
new file mode 100644
index 000000000..23233fd39
--- /dev/null
+++ b/packages/docutils/rstast.nim
@@ -0,0 +1,288 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements an AST for the `reStructuredText`:idx parser.
+
+import strutils
+
+type
+  TRstNodeKind* = enum        ## the possible node kinds of an PRstNode
+    rnInner,                  # an inner node or a root
+    rnHeadline,               # a headline
+    rnOverline,               # an over- and underlined headline
+    rnTransition,             # a transition (the ------------- <hr> thingie)
+    rnParagraph,              # a paragraph
+    rnBulletList,             # a bullet list
+    rnBulletItem,             # a bullet item
+    rnEnumList,               # an enumerated list
+    rnEnumItem,               # an enumerated item
+    rnDefList,                # a definition list
+    rnDefItem,                # an item of a definition list consisting of ...
+    rnDefName,                # ... a name part ...
+    rnDefBody,                # ... and a body part ...
+    rnFieldList,              # a field list
+    rnField,                  # a field item
+    rnFieldName,              # consisting of a field name ...
+    rnFieldBody,              # ... and a field body
+    rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, 
+    rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock,
+    rnLineBlock,              # the | thingie
+    rnLineBlockItem,          # sons of the | thing
+    rnBlockQuote,             # text just indented
+    rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
+    rnLabel,                  # used for footnotes and other things
+    rnFootnote,               # a footnote
+    rnCitation,               # similar to footnote
+    rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive
+    rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock,
+    rnRawHtml, rnRawLatex,
+    rnContainer,              # ``container`` directive
+    rnIndex,                  # index directve:
+                              # .. index::
+                              #   key
+                              #     * `file#id <file#id>`_
+                              #     * `file#id <file#id>'_
+    rnSubstitutionDef,        # a definition of a substitution
+    rnGeneralRole,            # Inline markup:
+    rnSub, rnSup, rnIdx, 
+    rnEmphasis,               # "*"
+    rnStrongEmphasis,         # "**"
+    rnTripleEmphasis,         # "***"
+    rnInterpretedText,        # "`"
+    rnInlineLiteral,          # "``"
+    rnSubstitutionReferences, # "|"
+    rnSmiley,                 # some smiley
+    rnLeaf                    # a leaf; the node's text field contains the
+                              # leaf val
+
+
+  PRSTNode* = ref TRstNode    ## an RST node
+  TRstNodeSeq* = seq[PRstNode]
+  TRSTNode* {.acyclic, final.} = object ## an RST node's description
+    kind*: TRstNodeKind       ## the node's kind
+    text*: string             ## valid for leafs in the AST; and the title of
+                              ## the document or the section
+    level*: int               ## valid for some node kinds
+    sons*: TRstNodeSeq        ## the node's sons
+
+proc len*(n: PRstNode): int = 
+  result = len(n.sons)
+
+proc newRstNode*(kind: TRstNodeKind): PRstNode = 
+  new(result)
+  result.sons = @[]
+  result.kind = kind
+
+proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode = 
+  result = newRstNode(kind)
+  result.text = s
+
+proc lastSon*(n: PRstNode): PRstNode = 
+  result = n.sons[len(n.sons)-1]
+
+proc add*(father, son: PRstNode) =
+  add(father.sons, son)
+
+proc addIfNotNil*(father, son: PRstNode) = 
+  if son != nil: add(father, son)
+
+
+type
+  TRenderContext {.pure.} = object
+    indent: int
+    verbatim: int
+
+proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string)
+
+proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) = 
+  for i in countup(0, len(n) - 1): 
+    renderRstToRst(d, n.sons[i], result)
+  
+proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
+  # this is needed for the index generation; it may also be useful for
+  # debugging, but most code is already debugged...
+  const 
+    lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
+  if n == nil: return
+  var ind = repeatChar(d.indent)
+  case n.kind
+  of rnInner: 
+    renderRstSons(d, n, result)
+  of rnHeadline:
+    result.add("\n")
+    result.add(ind)
+    
+    let oldLen = result.len
+    renderRstSons(d, n, result)
+    let HeadlineLen = result.len - oldLen
+
+    result.add("\n")
+    result.add(ind)
+    result.add repeatChar(HeadlineLen, lvlToChar[n.level])
+  of rnOverline:
+    result.add("\n")
+    result.add(ind)
+
+    var headline = ""
+    renderRstSons(d, n, headline)
+    
+    let lvl = repeatChar(headline.Len - d.indent, lvlToChar[n.level])
+    result.add(lvl)
+    result.add("\n")
+    result.add(headline)
+    
+    result.add("\n")
+    result.add(ind)
+    result.add(lvl)
+  of rnTransition: 
+    result.add("\n\n")
+    result.add(ind)
+    result.add repeatChar(78-d.indent, '-')
+    result.add("\n\n")
+  of rnParagraph:
+    result.add("\n\n")
+    result.add(ind)
+    renderRstSons(d, n, result)
+  of rnBulletItem: 
+    inc(d.indent, 2)
+    var tmp = ""
+    renderRstSons(d, n, tmp)
+    if tmp.len > 0: 
+      result.add("\n")
+      result.add(ind)
+      result.add("* ")
+      result.add(tmp)
+    dec(d.indent, 2)
+  of rnEnumItem:
+    inc(d.indent, 4)
+    var tmp = ""
+    renderRstSons(d, n, tmp)
+    if tmp.len > 0: 
+      result.add("\n")
+      result.add(ind)
+      result.add("(#) ")
+      result.add(tmp)
+    dec(d.indent, 4)
+  of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, 
+     rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: 
+    renderRstSons(d, n, result)
+  of rnDefName: 
+    result.add("\n\n")
+    result.add(ind)
+    renderRstSons(d, n, result)
+  of rnDefBody:
+    inc(d.indent, 2)
+    if n.sons[0].kind != rnBulletList: 
+      result.add("\n")
+      result.add(ind)
+      result.add("  ")
+    renderRstSons(d, n, result)
+    dec(d.indent, 2)
+  of rnField:
+    var tmp = ""
+    renderRstToRst(d, n.sons[0], tmp)
+    
+    var L = max(tmp.len + 3, 30)
+    inc(d.indent, L)
+    
+    result.add "\n"
+    result.add ind
+    result.add ':'
+    result.add tmp
+    result.add ':'
+    result.add repeatChar(L - tmp.len - 2)
+    renderRstToRst(d, n.sons[1], result)
+    
+    dec(d.indent, L)
+  of rnLineBlockItem: 
+    result.add("\n")
+    result.add(ind)
+    result.add("| ")
+    renderRstSons(d, n, result)
+  of rnBlockQuote:
+    inc(d.indent, 2)
+    renderRstSons(d, n, result)
+    dec(d.indent, 2)
+  of rnRef: 
+    result.add("`")
+    renderRstSons(d, n, result)
+    result.add("`_")
+  of rnHyperlink: 
+    result.add('`')
+    renderRstToRst(d, n.sons[0], result)
+    result.add(" <")
+    renderRstToRst(d, n.sons[1], result)
+    result.add(">`_")
+  of rnGeneralRole:
+    result.add('`')
+    renderRstToRst(d, n.sons[0],result)
+    result.add("`:")
+    renderRstToRst(d, n.sons[1],result)
+    result.add(':')
+  of rnSub: 
+    result.add('`')
+    renderRstSons(d, n, result)
+    result.add("`:sub:")
+  of rnSup: 
+    result.add('`')
+    renderRstSons(d, n, result)
+    result.add("`:sup:")
+  of rnIdx: 
+    result.add('`')
+    renderRstSons(d, n, result)
+    result.add("`:idx:")
+  of rnEmphasis: 
+    result.add("*")
+    renderRstSons(d, n, result)
+    result.add("*")
+  of rnStrongEmphasis: 
+    result.add("**")
+    renderRstSons(d, n, result)
+    result.add("**")
+  of rnTripleEmphasis:
+    result.add("***")
+    renderRstSons(d, n, result)
+    result.add("***")
+  of rnInterpretedText: 
+    result.add('`')
+    renderRstSons(d, n, result)
+    result.add('`')
+  of rnInlineLiteral: 
+    inc(d.verbatim)
+    result.add("``")
+    renderRstSons(d, n, result)
+    result.add("``")
+    dec(d.verbatim)
+  of rnSmiley:
+    result.add(n.text)
+  of rnLeaf:
+    if d.verbatim == 0 and n.text == "\\":
+      result.add("\\\\") # XXX: escape more special characters!
+    else:
+      result.add(n.text)
+  of rnIndex: 
+    result.add("\n\n")
+    result.add(ind)
+    result.add(".. index::\n")
+    
+    inc(d.indent, 3)
+    if n.sons[2] != nil: renderRstSons(d, n.sons[2], result)
+    dec(d.indent, 3)
+  of rnContents:
+    result.add("\n\n")
+    result.add(ind)
+    result.add(".. contents::")
+  else:
+    result.add("Error: cannot render: " & $n.kind)
+  
+proc renderRstToRst*(n: PRstNode, result: var string) =
+  ## renders `n` into its string representation and appends to `result`.
+  var d: TRenderContext
+  renderRstToRst(d, n, result)
+
diff --git a/packages/docutils/rstgen.nim b/packages/docutils/rstgen.nim
new file mode 100644
index 000000000..93544124f
--- /dev/null
+++ b/packages/docutils/rstgen.nim
@@ -0,0 +1,87 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a generator of HTML/Latex from `reStructuredText`:idx.
+
+import strutils, strtabs, rstast
+
+type
+  TOutputTarget* = enum ## which document type to generate
+    outHtml,            # output is HTML
+    outLatex            # output is Latex
+    
+
+proc addXmlChar(dest: var string, c: Char) = 
+  case c
+  of '&': add(dest, "&amp;")
+  of '<': add(dest, "&lt;")
+  of '>': add(dest, "&gt;")
+  of '\"': add(dest, "&quot;")
+  else: add(dest, c)
+  
+proc addRtfChar(dest: var string, c: Char) = 
+  case c
+  of '{': add(dest, "\\{")
+  of '}': add(dest, "\\}")
+  of '\\': add(dest, "\\\\")
+  else: add(dest, c)
+  
+proc addTexChar(dest: var string, c: Char) = 
+  case c
+  of '_': add(dest, "\\_")
+  of '{': add(dest, "\\symbol{123}")
+  of '}': add(dest, "\\symbol{125}")
+  of '[': add(dest, "\\symbol{91}")
+  of ']': add(dest, "\\symbol{93}")
+  of '\\': add(dest, "\\symbol{92}")
+  of '$': add(dest, "\\$")
+  of '&': add(dest, "\\&")
+  of '#': add(dest, "\\#")
+  of '%': add(dest, "\\%")
+  of '~': add(dest, "\\symbol{126}")
+  of '@': add(dest, "\\symbol{64}")
+  of '^': add(dest, "\\symbol{94}")
+  of '`': add(dest, "\\symbol{96}")
+  else: add(dest, c)
+
+var splitter*: string = "<wbr />"
+
+proc escChar*(target: TOutputTarget, dest: var string, c: Char) {.inline.} = 
+  case target
+  of outHtml:  addXmlChar(dest, c)
+  of outLatex: addTexChar(dest, c)
+  
+proc nextSplitPoint*(s: string, start: int): int = 
+  result = start
+  while result < len(s) + 0: 
+    case s[result]
+    of '_': return 
+    of 'a'..'z': 
+      if result + 1 < len(s) + 0: 
+        if s[result + 1] in {'A'..'Z'}: return 
+    else: nil
+    inc(result)
+  dec(result)                 # last valid index
+  
+proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string = 
+  result = ""
+  if splitAfter >= 0: 
+    var partLen = 0
+    var j = 0
+    while j < len(s): 
+      var k = nextSplitPoint(s, j)
+      if (splitter != " ") or (partLen + k - j + 1 > splitAfter): 
+        partLen = 0
+        add(result, splitter)
+      for i in countup(j, k): escChar(target, result, s[i])
+      inc(partLen, k - j + 1)
+      j = k + 1
+  else: 
+    for i in countup(0, len(s) - 1): escChar(target, result, s[i])
+  
\ No newline at end of file
diff --git a/web/news.txt b/web/news.txt
index 578e2203c..cff4bcbec 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -42,9 +42,10 @@ Library Additions
 - Added overload for ``system.items`` that can be used to iterate over the
   values of an enum.
 - Added ``system.TInteger`` and ``system.TNumber`` type classes matching
-  any of the corresponding type available in nimrod.
+  any of the corresponding types available in nimrod.
 - Added ``system.clamp`` to limit a value within an interval ``[a, b]``.
 - Added ``strutils.continuesWith``.
+- Added ``system.getStackTrace``.
 - The GC supports (soft) realtime systems via ``GC_setMaxPause`` 
   and ``GC_step`` procs.