summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorrumpf_a@web.de <>2009-10-21 10:20:15 +0200
committerrumpf_a@web.de <>2009-10-21 10:20:15 +0200
commit053309e60aee1eda594a4817ac8ac2fb8c18fb04 (patch)
tree0f1ce8b0de0b493045eb97eeca6ebf06542de601
parent581572b28c65bc9fe47974cfd625210a69be0f3f (diff)
downloadNim-053309e60aee1eda594a4817ac8ac2fb8c18fb04.tar.gz
version 0.8.2
-rwxr-xr-xboot.nim12
-rwxr-xr-xconfig/nimrod.cfg16
-rwxr-xr-xdata/advopt.txt4
-rwxr-xr-xdata/ast.yml5
-rwxr-xr-xdata/basicopt.txt2
-rwxr-xr-xdata/messages.yml7
-rwxr-xr-x[-rw-r--r--]doc/apis.txt3
-rwxr-xr-xdoc/effects.txt41
-rwxr-xr-xdoc/filelist.txt3
-rwxr-xr-xdoc/filters.txt232
-rwxr-xr-xdoc/gramcurl.txt179
-rwxr-xr-xdoc/grammar.txt96
-rwxr-xr-xdoc/lib.txt3
-rwxr-xr-xdoc/manual.txt97
-rwxr-xr-xdoc/nimrodc.txt13
-rwxr-xr-xdoc/pegdocs.txt180
-rwxr-xr-xdoc/theindex.txt414
-rwxr-xr-xdoc/tut1.txt8
-rwxr-xr-xdoc/tut2.txt7
-rwxr-xr-xexamples/filterex.nim23
-rwxr-xr-xexamples/iupex1.nim37
-rwxr-xr-xexamples/keyval2.nim7
-rwxr-xr-xexamples/transff.nim8
-rwxr-xr-xinstall.txt9
-rwxr-xr-xkoch.py7
-rwxr-xr-xlib/impure/db_postgres.nim129
-rwxr-xr-xlib/nimbase.h6
-rwxr-xr-xlib/posix/posix.nim2
-rwxr-xr-xlib/pure/hashtabs.nim163
-rwxr-xr-xlib/pure/macros.nim45
-rwxr-xr-xlib/pure/os.nim54
-rwxr-xr-xlib/pure/osproc.nim266
-rwxr-xr-xlib/pure/pegs.nim1320
-rwxr-xr-xlib/pure/re.nim353
-rwxr-xr-xlib/pure/regexprs.nim3
-rwxr-xr-xlib/pure/streams.nim5
-rwxr-xr-xlib/pure/strutils.nim91
-rwxr-xr-xlib/pure/variants.nim181
-rwxr-xr-xlib/system.nim16
-rwxr-xr-xlib/system/mm.nim33
-rwxr-xr-xlib/windows/winlean.nim6
-rwxr-xr-x[-rw-r--r--]lib/wrappers/tre/config.h0
-rwxr-xr-x[-rw-r--r--]lib/wrappers/tre/tre_all.c0
-rwxr-xr-x[-rw-r--r--]lib/wrappers/tre/version.txt0
-rwxr-xr-xnim/ast.pas87
-rwxr-xr-xnim/ccgexprs.pas4
-rwxr-xr-xnim/ccgtypes.pas3
-rwxr-xr-xnim/cgen.pas8
-rwxr-xr-x[-rw-r--r--]nim/cgmeth.pas0
-rwxr-xr-xnim/commands.pas20
-rwxr-xr-xnim/depends.pas4
-rwxr-xr-xnim/docgen.pas8
-rwxr-xr-xnim/ecmasgen.pas2
-rwxr-xr-xnim/evals.pas2
-rwxr-xr-xnim/extccomp.pas79
-rwxr-xr-xnim/filters.pas137
-rwxr-xr-xnim/importer.pas5
-rwxr-xr-xnim/llstream.pas61
-rwxr-xr-xnim/lookups.pas4
-rwxr-xr-xnim/main.pas31
-rwxr-xr-xnim/msgs.pas15
-rwxr-xr-xnim/nimrod.pas20
-rwxr-xr-xnim/nos.pas18
-rwxr-xr-xnim/nsystem.pas18
-rwxr-xr-xnim/nversion.pas4
-rwxr-xr-xnim/options.pas12
-rwxr-xr-xnim/osproc.pas36
-rwxr-xr-xnim/pasparse.pas14
-rwxr-xr-xnim/passes.pas11
-rwxr-xr-xnim/pbraces.pas1499
-rwxr-xr-xnim/platform.pas68
-rwxr-xr-xnim/pnimsyn.pas528
-rwxr-xr-xnim/pragmas.pas21
-rwxr-xr-xnim/ptmplsyn.pas210
-rwxr-xr-xnim/rnimsyn.pas4
-rwxr-xr-xnim/rodread.pas3
-rwxr-xr-xnim/rst.pas3
-rwxr-xr-xnim/scanner.pas8
-rwxr-xr-xnim/semexprs.pas59
-rwxr-xr-xnim/semstmts.pas18
-rwxr-xr-xnim/semtempl.pas2
-rwxr-xr-xnim/semtypes.pas6
-rwxr-xr-xnim/strutils.pas7
-rwxr-xr-xnim/syntaxes.pas240
-rwxr-xr-xnim/transf.pas11
-rwxr-xr-xnim/wordrecg.pas8
-rwxr-xr-xrod/nimrod.ini2
-rwxr-xr-xtests/sunset.tmpl1
-rwxr-xr-xtests/tadrdisc.nim16
-rwxr-xr-xtests/tbind1.nim14
-rwxr-xr-xtests/tbind2.nim10
-rwxr-xr-xtests/tbintre2.nim25
-rwxr-xr-xtests/tcasestm.nim20
-rwxr-xr-xtests/tcnstseq.nim11
-rwxr-xr-xtests/tcopy.nim2
-rwxr-xr-xtests/tester.nim2
-rwxr-xr-xtests/titer4.nim3
-rwxr-xr-xtests/tmultim1.nim23
-rwxr-xr-xtests/tmultim2.nim30
-rwxr-xr-xtests/tobjcov.nim17
-rwxr-xr-xtests/toverl2.nim21
-rwxr-xr-xtests/toverprc.nim25
-rwxr-xr-xtests/tposix.nim19
-rwxr-xr-xtests/treadx.nim12
-rwxr-xr-xtests/trecinca.nim5
-rwxr-xr-xtests/trecincb.nim6
-rwxr-xr-xtests/treciter.nim7
-rwxr-xr-xtests/tseqcon2.nim9
-rwxr-xr-xtests/tsidee1.nim11
-rwxr-xr-xtests/tsidee2.nim11
-rwxr-xr-xtests/tsidee3.nim11
-rwxr-xr-xtests/tsidee4.nim10
-rwxr-xr-xtests/tvartup.nim11
-rwxr-xr-xtools/build.tmpl2
-rwxr-xr-xtools/deinstall.tmpl2
-rwxr-xr-xtools/inno.tmpl1
-rwxr-xr-xtools/install.tmpl2
-rwxr-xr-xtools/niminst.nim9
-rwxr-xr-xtools/nimweb.nim36
-rwxr-xr-xtools/sunset.tmpl1
-rwxr-xr-xweb/download.txt4
-rwxr-xr-xweb/index.txt12
-rwxr-xr-xweb/news.txt65
-rwxr-xr-xweb/nimrod.ini1
-rwxr-xr-xweb/ticker.txt5
125 files changed, 6557 insertions, 1301 deletions
diff --git a/boot.nim b/boot.nim
index b7f3f4ed9..dbcf84399 100755
--- a/boot.nim
+++ b/boot.nim
@@ -23,7 +23,7 @@ char* nimCPU(void) { return "$2"; }
 
 proc exec(cmd: string) =
   echo(cmd)
-  if executeShellCommand(cmd) != 0: quit("FAILURE")
+  if execShellCmd(cmd) != 0: quit("FAILURE")
 
 proc writePlatdefC =
   var f: TFile
@@ -38,19 +38,19 @@ proc rodsrc =
     blacklist = ["nsystem", "nmath", "nos", "osproc", "ntime", "strutils"]
     cmd = "nimrod boot --skip_proj_cfg -o:rod/$1.nim nim/$1"
   for fi in walkFiles("nim/*.pas"):
-    var f = extractFileTrunk(fi)
+    var f = splitFile(fi).name
     if find(blacklist, f) >= 0: continue
-    var r = "rod" / appendFileExt(f, "nim")
+    var r = "rod" / addFileExt(f, "nim")
     if not existsFile(r) or fileNewer(fi, r):
       Exec(cmd % f)
   
 proc boot(args: string) =
   writePlatdefC()
   rodsrc()
-  var newExe = appendFileExt("rod/nimrod", ExeExt)
-  var oldExe = appendFileExt("bin/nimrod", ExeExt)
+  var newExe = addFileExt("rod/nimrod", ExeExt)
+  var oldExe = addFileExt("bin/nimrod", ExeExt)
   for i in 0..1:
-    Echo("iteration: ", $(i+1))
+    Echo("iteration: ", i+1)
     # use the new executable to compile the files in the bootstrap directory:
     Exec(Bootcmd[i] % args)
     if sameFileContent(newExe, oldExe): 
diff --git a/config/nimrod.cfg b/config/nimrod.cfg
index 6f6f89bc8..c1ad11380 100755
--- a/config/nimrod.cfg
+++ b/config/nimrod.cfg
@@ -43,18 +43,20 @@ path="$lib/ecmas"
 

 # additional options always passed to the compiler:

 --verbosity: "1"

+--parallel_build: "0" # 0 to auto-detect number of processors
 

 hint[LineTooLong]=off

 #hint[XDeclaredButNotUsed]=off

 

 @if unix:

-  @if not bsd: passl= "-ldl" @end

+  @if not bsd:

+    gcc.options.linker = "-ldl"

+  @end

 @end

 

-@if icc:

-  passl = "-cxxlib"

-  passc = "-cxxlib"

-@end

+# Configuration for the Intel C/C++ compiler:

+icc.options.always = "-cxxlib"

+icc.options.linker = "-cxxlib"

 

 # Configuration for the GNU C/C++ compiler:

 @if windows:

@@ -77,9 +79,7 @@ llvm_gcc.options.speed = "-O2"
 llvm_gcc.options.size = "-Os"

 

 # Configuration for the Visual C/C++ compiler:

-@if vcc:

-  passl: r"/F33554432" # set the stack size to 8 MB

-@end

+vcc.options.linker = r"/F33554432" # set the stack size to 8 MB

 vcc.options.debug = "/RTC1 /ZI"

 vcc.options.always = "/nologo"

 vcc.options.speed = "/Ox /arch:SSE2"

diff --git a/data/advopt.txt b/data/advopt.txt
index 72e8ffb7c..975c09af8 100755
--- a/data/advopt.txt
+++ b/data/advopt.txt
@@ -28,9 +28,11 @@ Advanced options:
   --checkpoints:on|off      turn on|off checkpoints; for debugging Nimrod
   --skip_cfg                do not read the general configuration file
   --skip_proj_cfg           do not read the project's configuration file
-  --gc:refc|boehm           use Nimrod's native GC|Boehm GC
+  --gc:refc|boehm|none      use Nimrod's native GC|Boehm GC|no GC
   --index:FILE              use FILE to generate a documenation index file
   --putenv:key=value        set an environment variable
   --list_cmd                list the commands used to execute external programs
+  --parallel_build=0|1|...  perform a parallel build
+                            value = number of processors (0 for auto-detect)
   --verbosity:0|1|2|3       set Nimrod's verbosity level (0 is default)
   -v, --version             show detailed version information
diff --git a/data/ast.yml b/data/ast.yml
index 805b5dc68..f27b09a18 100755
--- a/data/ast.yml
+++ b/data/ast.yml
@@ -8,7 +8,7 @@
 #
 
 {
-'SymFlag': [          # already 29 flags!
+'SymFlag': [          # already 30 flags!
   'sfUsed',           # read access of sym (for warnings) or simply used
   'sfStar',           # symbol has * visibility
   'sfMinus',          # symbol has - visibility
@@ -34,7 +34,7 @@
   'sfAddrTaken',      # the variable's address is taken (ex- or implicitely)
   'sfCompilerProc',   # proc is a compiler proc, that is a C proc that is
                       # needed for the code generator
-  'sfCppMethod',      # proc is a C++ method (not implemented yet)
+  'sfProcvar',        # proc can be passed to a proc var
   'sfDiscriminant',   # field is a discriminant in a record/object
   'sfDeprecated',     # symbol is deprecated
   'sfInClosure',      # variable is accessed by a closure
@@ -152,7 +152,6 @@
   'nkAccQuoted',          # `a` as a node
 
   'nkTableConstr',        # a table constructor {expr: expr}
-  'nkQualified',          # describes a.b for qualified identifiers
   'nkBind',               # ``bind expr`` node
   'nkSymChoice',          # symbol choice node
   'nkHiddenStdConv',      # an implicit standard type conversion
diff --git a/data/basicopt.txt b/data/basicopt.txt
index c716f7641..af3f541ad 100755
--- a/data/basicopt.txt
+++ b/data/basicopt.txt
@@ -27,7 +27,7 @@ Options:
   -a, --assertions:on|off   code generation for assertions ON|OFF
   --dead_code_elim:on|off   whole program dead code elimination ON|OFF
   --opt:none|speed|size     optimize not at all or for speed|size
-  --app:console|gui         generate a console|GUI application
+  --app:console|gui|lib     generate a console|GUI application|dynamic library
   -r, --run                 run the compiled program with given arguments
   --advanced                show advanced command line switches
   -h, --help                show this help
diff --git a/data/messages.yml b/data/messages.yml
index 44e05dca9..ac41b5031 100755
--- a/data/messages.yml
+++ b/data/messages.yml
@@ -92,7 +92,7 @@
 {'errCannotEvalXBecauseIncompletelyDefined':
   "cannot evalutate '$1' because type is not defined completely"},
 {'errChrExpectsRange0_255': "'chr' expects an int in the range 0..255"},
-{'errDotRequiresRecordOrObjectType': "'.' requires a record or object type"},
+{'errDynlibRequiresExportc': "'dynlib' requires 'exportc'"},
 {'errUndeclaredFieldX': "undeclared field: '$1'"},
 {'errNilAccess': 'attempt to access a nil address'},
 {'errIndexOutOfBounds': 'index out of bounds'},
@@ -152,7 +152,7 @@
 {'errButExpectedX': "but expected '$1'"},
 {'errAmbiguousCallXYZ': 'ambiguous call; both $1 and $2 match for: $3'},
 {'errWrongNumberOfArguments': 'wrong number of arguments'},
-{'errInlineProcHasNoAddress': 'an inline proc has no address'},
+{'errXCannotBePassedToProcVar': "'$1' cannot be passed to a procvar"},
 {'errXCannotBeInParamDecl': '$1 cannot be declared in parameter declaration'},
 {'errPragmaOnlyInHeaderOfProc':
   'pragmas are only in the header of a proc allowed'},
@@ -186,7 +186,6 @@
 {'errATypeHasNoValue': 'a type has no value'},
 {'errXisNoType': "invalid type: '$1'"},
 {'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
-{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
 {'errInvalidExpression': 'invalid expression'},
 {'errInvalidExpressionX': "invalid expression: '$1'"},
 {'errEnumHasNoValueX': "enum has no value '$1'"},
@@ -250,6 +249,8 @@
 {'warnUnknownSubstitutionX': "unknown substitution '$1'"},
 {'warnLanguageXNotSupported': "language '$1' not supported"},
 {'warnCommentXIgnored': "comment '$1' ignored"},
+{'warnXisPassedToProcVar': "'$1' is passed to a procvar; deprecated"},
+
 # user warning message:
 {'warnUser': '$1'},
 
diff --git a/doc/apis.txt b/doc/apis.txt
index 02caa8c24..b926e4988 100644..100755
--- a/doc/apis.txt
+++ b/doc/apis.txt
@@ -66,4 +66,7 @@ function                func
 coordinate              coord
 rectangle               rect
 point                   point
+symbol                  sym
+identifier              ident
+indentation             indent
 -------------------     ------------   --------------------------------------
diff --git a/doc/effects.txt b/doc/effects.txt
new file mode 100755
index 000000000..85de1ffdf
--- /dev/null
+++ b/doc/effects.txt
@@ -0,0 +1,41 @@
+=====================================================================
+               Side effects in Nimrod
+=====================================================================
+
+Note: Side effects are implicit produced values! Maybe they should be
+explicit like in Haskell?
+
+
+The idea is that side effects and partial evaluation belong together:
+Iff a proc is side effect free and all its argument are evaluable at
+compile time, it can be evaluated by the compiler. However, really
+difficult is the ``newString`` proc: If it is simply wrapped, it 
+should not be evaluated at compile time! On other occasions it can
+and should be evaluted:
+
+.. code-block:: nimrod
+  proc toUpper(s: string): string =
+    result = newString(len(s))
+    for i in 0..len(s) - 1:
+      result[i] = toUpper(s[i])
+
+No, it really can always be evaluated. The code generator should transform
+``s = "\0\0\0..."`` back into ``s = newString(...)``. 
+
+
+``new`` cannot be evaluated at compile time either. 
+
+
+Raise statement
+===============
+
+It is impractical to consider ``raise`` a statement with side effects.
+
+
+Solution
+========
+
+Being side effect free does not suffice for compile time evaluation. However,
+the evaluator can attempt to evaluate at compile time. 
+
+
diff --git a/doc/filelist.txt b/doc/filelist.txt
index 0e636652a..c29b3b6aa 100755
--- a/doc/filelist.txt
+++ b/doc/filelist.txt
@@ -8,7 +8,8 @@ nimrod          main module: parses the command line and calls
                 ``main.MainCommand``
 main            implements the top-level command dispatching
 nimconf         implements the config file reader
-
+syntaxes        dispatcher for the different parsers and filters
+ptmplsyn        standard template filter (``#! stdtempl``)
 lexbase         buffer handling of the lexical analyser
 scanner         lexical analyser
 pnimsyn         Nimrod's parser
diff --git a/doc/filters.txt b/doc/filters.txt
new file mode 100755
index 000000000..ffeb6808a
--- /dev/null
+++ b/doc/filters.txt
@@ -0,0 +1,232 @@
+===================
+Parsers and Filters
+===================
+
+.. contents::
+
+The Nimrod compiler contains multiple parsers. (The standard is 
+indentation-based.) Two others are available: The `braces`:idx: parser and the
+`endX`:idx: parser. Both parsers use the same lexer as the standard parser.
+
+To use a different parser for a source file the *shebang* notation is used:
+
+.. code-block:: nimrod
+  #! braces
+  if (x == 10) {
+    echo "x is ten"
+  } else {
+    echo "x isn't ten"
+  }
+
+The special ``#!`` comment for specifying a parser needs to be in the first
+line with no leading whitespace, unless an UNIX shebang line is used. Then the
+parser shebang can occur in the second line:
+
+.. code-block:: nimrod
+  #! /usr/bin/env nimrod c -r
+  #! braces
+  if (x == 10) {
+    echo "x is ten"
+  } else {
+    echo "x isn't ten"
+  }
+
+An UNIX shebang line is defined by the pattern ``'#!' \s* '/' .*`` 
+(``#!`` followed by optional whitespace followed by ``/``). 
+
+
+Filters
+=======
+
+Nimrod's shebang also supports the invokation of `source filters`:idx: before
+the source code file is passed to the parser::
+
+  #! stdtmpl(subsChar = '$', metaChar = '#')
+  #proc generateXML(name, age: string): string =
+  #  result = ""
+  <xml>
+    <name>$name</name>
+    <age>$age</age>
+  </xml>
+
+Filters transform the input character stream to an in-memory output stream.
+They are used to provide templating systems or preprocessors. 
+
+As the example shows, passing arguments to a filter (or parser) can be done
+just like an ordinary procedure call with named or positional arguments. The
+available parameters depend on the invoked filter/parser.
+
+
+Pipe operator
+-------------
+
+Filters and parsers can be combined with the ``|`` `pipe operator`:idx:. Only
+the last operand can be a parser because a parser returns an abstract syntax
+tree which a filter cannot process::
+
+  #! strip(startswith="<") | stdtmpl | standard
+  #proc generateXML(name, age: string): string =
+  #  result = ""
+  <xml>
+    <name>$name</name>
+    <age>$age</age>
+  </xml>
+
+
+Available filters
+=================
+
+**Hint:** With ``--verbosity:2`` (or higher) Nimrod lists the processed code
+after each filter application.
+
+Replace filter
+--------------
+
+The `replace`:idx: filter replaces substrings in each line.
+
+Parameters and their defaults:
+
+  ``sub: string = ""``
+    the substring that is searched for
+  
+  ``by: string = ""``
+    the string the substring is replaced with
+
+
+Strip filter
+------------
+
+The `strip`:idx: filter simply removes leading and trailing whitespace from
+each line.
+
+Parameters and their defaults:
+
+  ``startswith: string = ""``
+    strip only the lines that start with *startswith* (ignoring leading
+    whitespace). If empty every line is stripped.
+
+  ``leading: bool = true``
+    strip leading whitespace
+  
+  ``trailing: bool = true``
+    strip trailing whitespace
+
+
+StdTmpl filter
+--------------
+
+The `stdtmpl`:idx: filter provides a simple templating engine for Nimrod. The
+filter uses a line based parser: Lines prefixed with a *meta character*
+(default: ``#``) contain Nimrod code, other lines are verbatim. Because
+indentation-based parsing is not suited for a templating engine, control flow
+statements need ``end X`` delimiters.
+
+Parameters and their defaults:
+
+  ``metaChar: char = '#'``
+    prefix for a line that contains Nimrod code
+  
+  ``subsChar: char = '$'``
+    prefix for a Nimrod expression within a template line
+    
+  ``conc: string = " & "``
+    the operation for concatenation
+    
+  ``emit: string = "result.add"``
+    the operation to emit a string literal
+    
+  ``toString: string = "$"``
+    the operation that is applied to each expression
+
+Example::
+
+  #! stdtmpl | standard
+  #proc generateHTMLPage(title, currentTab, content: string,
+  #                      tabs: openArray[string]): string = 
+  #  result = ""
+  <head><title>$title</title></head>
+  <body>
+    <div id="menu">
+      <ul>
+    #for tab in items(tabs):
+      #if currentTab == tab:
+      <li><a id="selected" 
+      #else:
+      <li><a
+      #end if
+      href="${tab}.html">$tab</a></li>
+    #end for
+      </ul>
+    </div>
+    <div id="content">
+      $content
+      A dollar: $$.
+    </div>
+  </body>
+
+The filter transforms this into:
+
+.. code-block:: nimrod
+  proc generateHTMLPage(title, currentTab, content: string,
+                        tabs: openArray[string]): string = 
+    result = ""
+    result.add("<head><title>" & $(title) & "</title></head>\n" & 
+      "<body>\n" & 
+      "  <div id=\"menu\">\n" & 
+      "    <ul>\n")
+    for tab in items(tabs):
+      if currentTab == tab:
+        result.add("    <li><a id=\"selected\" \n")
+      else:
+        result.add("    <li><a\n")
+      #end
+      result.add("    href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
+    #end
+    result.add("    </ul>\n" & 
+      "  </div>\n" & 
+      "  <div id=\"content\">\n" & 
+      "    " & $(content) & "\n" & 
+      "    A dollar: $.\n" & 
+      "  </div>\n" & 
+      "</body>\n")
+
+  
+Each line that does not start with the meta character (ignoring leading
+whitespace) is converted to a string literal that is added to ``result``. 
+
+The substitution character introduces a Nimrod expression *e* within the
+string literal. *e* is converted to a string with the *toString* operation
+which defaults to ``$``. For strong type checking, set ``toString`` to the
+empty string. *e* must match this PEG pattern::
+
+  e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
+  x <- '{' x+ '}' / [^}]*
+
+To produce a single substitution character it has to be doubled: ``$$``
+produces ``$``.
+
+The template engine is quite flexible. It is easy to produce a procedure that
+writes the template code directly to a file::
+
+  #! stdtmpl(emit="f.write") | standard
+  #proc writeHTMLPage(f: TFile, title, currentTab, content: string,
+  #                   tabs: openArray[string]) = 
+  <head><title>$title</title></head>
+  <body>
+    <div id="menu">
+      <ul>
+    #for tab in items(tabs):
+      #if currentTab == tab:
+      <li><a id="selected" 
+      #else:
+      <li><a
+      #end if
+      href="${tab}.html" title = "$title - $tab">$tab</a></li>
+    #end for
+      </ul>
+    </div>
+    <div id="content">
+      $content
+      A dollar: $$.
+    </div>
+  </body>
diff --git a/doc/gramcurl.txt b/doc/gramcurl.txt
new file mode 100755
index 000000000..3ac9294c8
--- /dev/null
+++ b/doc/gramcurl.txt
@@ -0,0 +1,179 @@
+module ::= stmt*
+
+comma ::= ',' [COMMENT] [IND]
+operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | OP6 | OP7
+           | 'is' | 'isnot' | 'in' | 'notin'
+           | 'div' | 'mod' | 'shl' | 'shr' | 'not'
+
+prefixOperator ::= OP0 | OP3 | OP4 | OP5 | OP6 | OP7 | 'not'
+
+optInd ::= [COMMENT] [IND]
+
+
+lowestExpr ::= orExpr (OP0 optInd orExpr)*
+orExpr ::= andExpr (OR | 'xor' optInd andExpr)*
+andExpr ::= cmpExpr ('and' optInd cmpExpr)*
+cmpExpr ::= ampExpr (OP3 | 'is' | 'isnot' | 'in' | 'notin' optInd ampExpr)*
+ampExpr ::= plusExpr (OP4 optInd plusExpr)*
+plusExpr ::= mulExpr (OP5 optInd mulExpr)*
+mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
+dollarExpr ::= primary (OP7 optInd primary)*
+
+indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
+
+castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
+addrExpr ::= 'addr' '(' optInd expr ')'
+symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
+               | '[' ']' | '=' | literal)+ '`'
+         | IDENT
+         
+primaryPrefix ::= (prefixOperator | 'bind') optInd
+primarySuffix ::= '.' optInd symbol
+                | '(' optInd namedExprList [SAD] ')'
+                | '[' optInd [indexExpr (comma indexExpr)* [comma]] [SAD] ']'
+                | '^'
+                | pragma
+
+primary ::= primaryPrefix* (symbol | constructor | castExpr | addrExpr)
+            primarySuffix*
+
+literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
+          | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
+          | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
+          | CHAR_LIT
+          | NIL
+
+constructor ::= literal
+          | '[' optInd colonExprList [SAD] ']'
+          | '{' optInd sliceExprList [SAD] '}'
+          | '(' optInd colonExprList [SAD] ')'
+
+colonExpr ::= expr [':' expr]
+colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
+
+namedExpr ::= expr ['=' expr]
+namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
+
+sliceExpr ::= expr ['..' expr]
+sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
+
+exprOrType ::= lowestExpr
+            | 'if' '(' expr ')' expr ('elif' '(' expr ')' expr)* 'else' expr
+            | 'var' exprOrType
+            | 'ref' exprOrType
+            | 'ptr' exprOrType
+            | 'type' exprOrType
+            | 'tuple' tupleDesc
+
+expr ::= exprOrType
+     | 'proc' paramList [pragma] ['=' stmt] 
+
+qualifiedIdent ::= symbol ['.' symbol]
+
+typeDesc ::= exprOrType
+         | 'proc' paramList [pragma]
+
+macroStmt ::= '{' [stmt] '}' ('of' [sliceExprList] stmt
+                         |'elif' '(' expr ')' stmt
+                         |'except' '(' exceptList ')' stmt )*
+                         ['else' stmt]
+
+simpleStmt ::= returnStmt
+             | yieldStmt
+             | discardStmt
+             | raiseStmt
+             | breakStmt
+             | continueStmt
+             | pragma
+             | importStmt
+             | fromStmt
+             | includeStmt
+             | exprStmt
+complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
+              | blockStmt | asmStmt
+              | procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
+              | constSection | typeSection | whenStmt | varSection
+
+stmt ::= simpleStmt
+       | indPush (complexStmt | simpleStmt) (';' (complexStmt | simpleStmt))*
+         DED indPop
+
+exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)*] [macroStmt]]
+returnStmt ::= 'return' [expr]
+yieldStmt ::= 'yield' expr
+discardStmt ::= 'discard' expr
+raiseStmt ::= 'raise' [expr]
+breakStmt ::= 'break' [symbol]
+continueStmt ::= 'continue'
+ifStmt ::= 'if' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
+whenStmt ::= 'when' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
+caseStmt ::= 'case' '(' expr ')' ('of' sliceExprList ':' stmt)*
+                               ('elif' '(' expr ')' stmt)*
+                               ['else' stmt]
+whileStmt ::= 'while' '(' expr ')' stmt
+forStmt ::= 'for' '(' symbol (comma symbol)* 'in' expr ['..' expr] ')' stmt
+exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
+
+tryStmt ::= 'try' stmt
+           ('except' '(' exceptList ')' stmt)*
+           ['finally' stmt]
+asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
+blockStmt ::= 'block' [symbol] stmt
+filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
+importStmt ::= 'import' filename (comma filename)*
+includeStmt ::= 'include' filename (comma filename)*
+fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
+
+pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
+
+param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
+paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
+
+genericParam ::= symbol [':' typeDesc] ['=' expr]
+genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
+
+
+routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
+procDecl ::= 'proc' routineDecl
+macroDecl ::= 'macro' routineDecl
+iteratorDecl ::= 'iterator' routineDecl
+templateDecl ::= 'template' routineDecl
+methodDecl ::= 'method' routineDecl
+
+colonAndEquals ::= [':' typeDesc] '=' expr
+
+constDecl ::= symbol ['*'] [pragma] colonAndEquals ';' [COMMENT]
+constSection ::= 'const' [COMMENT] (constDecl | '{' constDecl+ '}')
+
+typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
+
+objectField ::= symbol ['*'] [pragma]
+objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
+                    [COMMENT|IND COMMENT]
+
+objectWhen ::= 'when' expr ':' [COMMENT] objectPart
+              ('elif' expr ':' [COMMENT] objectPart)*
+              ['else' ':' [COMMENT] objectPart]
+objectCase ::= 'case' expr ':' typeDesc [COMMENT]
+              ('of' sliceExprList ':' [COMMENT] objectPart)*
+              ['else' ':' [COMMENT] objectPart]
+
+objectPart ::= objectWhen | objectCase | objectIdentPart | 'nil'
+             | indPush objectPart (SAD objectPart)* DED indPop
+tupleDesc ::= '[' optInd [param (comma param)*] [SAD] ']'
+
+objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
+enumField ::= symbol ['=' expr]
+enumDef ::= 'enum' ['of' typeDesc] (enumField [comma] [COMMENT | IND COMMENT])+
+
+typeDecl ::= COMMENT
+           | symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
+
+typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
+
+colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
+varField ::= symbol ['*'] [pragma]
+varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
+varSection ::= 'var' (varPart
+                   | indPush (COMMENT|varPart)
+                     (SAD (COMMENT|varPart))* DED indPop)
diff --git a/doc/grammar.txt b/doc/grammar.txt
index 2fcb68236..18722116e 100755
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -19,25 +19,23 @@ plusExpr ::= mulExpr (OP5 optInd mulExpr)*
 mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
 dollarExpr ::= primary (OP7 optInd primary)*
 
-namedTypeOrExpr ::=
-  '..' [expr]
-  | expr ['=' (expr ['..' expr] | typeDescK | '..' [expr]) | '..' [expr]]
-  | typeDescK
+indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
 
 castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
 addrExpr ::= 'addr' '(' optInd expr ')'
 symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
                | '[' ']' | '=' | literal)+ '`'
          | IDENT
-primary ::= ((prefixOperator | 'bind') optInd)* (symbol | constructor |
-                                                 castExpr | addrExpr) (
-               '.' optInd symbol
-             | '(' optInd namedExprList [SAD] ')'
-             | '[' optInd
-               [namedTypeOrExpr (comma namedTypeOrExpr)* [comma]]
-               [SAD] ']'
-             | '^'
-             | pragma)*
+         
+primaryPrefix ::= (prefixOperator | 'bind') optInd
+primarySuffix ::= '.' optInd symbol
+                | '(' optInd namedExprList [SAD] ')'
+                | '[' optInd [indexExpr (comma indexExpr)* [comma]] [SAD] ']'
+                | '^'
+                | pragma
+
+primary ::= primaryPrefix* (symbol | constructor | castExpr | addrExpr)
+            primarySuffix*
 
 literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
           | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
@@ -59,24 +57,21 @@ namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
 sliceExpr ::= expr ['..' expr]
 sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
 
-anonymousProc ::= 'lambda' paramList [pragma] '=' stmt
-expr ::= lowestExpr
-     | anonymousProc
-     | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
+exprOrType ::= lowestExpr
+            | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
+            | 'var' exprOrType
+            | 'ref' exprOrType
+            | 'ptr' exprOrType
+            | 'type' exprOrType
+            | 'tuple' tupleDesc
 
-namedTypeDesc ::= typeDescK | expr ['=' (typeDescK | expr)]
-namedTypeDescList ::= [namedTypeDesc (comma namedTypeDesc)* [comma]]
+expr ::= exprOrType
+     | 'proc' paramList [pragma] ['=' stmt] 
 
 qualifiedIdent ::= symbol ['.' symbol]
 
-typeDescK ::= 'var' typeDesc
-            | 'ref' typeDesc
-            | 'ptr' typeDesc
-            | 'type' expr
-            | 'tuple' tupleDesc
-            | 'proc' paramList [pragma]
-
-typeDesc ::= typeDescK | primary
+typeDesc ::= exprOrType
+         | 'proc' paramList [pragma]
 
 macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
                          |'elif' expr ':' stmt
@@ -84,20 +79,20 @@ macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
                          ['else' ':' stmt]
 
 simpleStmt ::= returnStmt
-           | yieldStmt
-           | discardStmt
-           | raiseStmt
-           | breakStmt
-           | continueStmt
-           | pragma
-           | importStmt
-           | fromStmt
-           | includeStmt
-           | exprStmt
+             | yieldStmt
+             | discardStmt
+             | raiseStmt
+             | breakStmt
+             | continueStmt
+             | pragma
+             | importStmt
+             | fromStmt
+             | includeStmt
+             | exprStmt
 complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
-                 | blockStmt | asmStmt
-                 | procDecl | iteratorDecl | macroDecl | templateDecl
-                 | constSection | typeSection | whenStmt | varSection
+              | blockStmt | asmStmt
+              | procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
+              | constSection | typeSection | whenStmt | varSection
 
 indPush ::= IND # and push indentation onto the stack
 indPop ::= # pop indentation from the stack
@@ -141,25 +136,24 @@ paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
 genericParam ::= symbol [':' typeDesc] ['=' expr]
 genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
 
-procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
-             ['=' stmt]
-macroDecl ::= 'macro' symbol ['*'] [genericParams] paramList [pragma]
-             ['=' stmt]
-iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
-             ['=' stmt]
-templateDecl ::= 'template' symbol ['*'] [genericParams] paramList [pragma]
-             ['=' stmt]
+
+routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
+procDecl ::= 'proc' routineDecl
+macroDecl ::= 'macro' routineDecl
+iteratorDecl ::= 'iterator' routineDecl
+templateDecl ::= 'template' routineDecl
+methodDecl ::= 'method' routineDecl
 
 colonAndEquals ::= [':' typeDesc] '=' expr
 
 constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
             | COMMENT
 constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
-typeDef ::= typeDesc | objectDef | enumDef | 'abstract' typeDesc
+typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
 
 objectField ::= symbol ['*'] [pragma]
-objectIdentPart ::=
-  objectField (comma objectField)* ':' typeDesc [COMMENT|IND COMMENT]
+objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
+                    [COMMENT|IND COMMENT]
 
 objectWhen ::= 'when' expr ':' [COMMENT] objectPart
               ('elif' expr ':' [COMMENT] objectPart)*
diff --git a/doc/lib.txt b/doc/lib.txt
index c15303b99..b75dd446b 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -66,8 +66,7 @@ Generic Operating System Services
 * `os <os.html>`_
   Basic operating system facilities like retrieving environment variables,
   reading command line arguments, working with directories, running shell
-  commands, etc. This module is -- like any other basic library --
-  platform independant.
+  commands, etc.
 
 * `osproc <osproc.html>`_
   Module for process communication beyond ``os.execShellCmd``.
diff --git a/doc/manual.txt b/doc/manual.txt
index 3c24e4b1a..9cf1f3bc2 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -342,7 +342,7 @@ Syntax
 ======
 
 This section lists Nimrod's standard syntax in ENBF. How the parser receives
-indentation tokens is already described in the Lexical Analysis section.
+indentation tokens is already described in the `Lexical Analysis`_ section.
 
 Nimrod allows user-definable operators.
 Binary operators have 8 different levels of precedence. For user-defined
@@ -363,8 +363,7 @@ Precedence level    Operators                                     First characte
 ================  ==============================================  ==================  ===============
 
 
-The grammar's start symbol is ``module``. The grammar is LL(1) and therefore
-not ambiguous.
+The grammar's start symbol is ``module``.
 
 .. include:: grammar.txt
    :literal:
@@ -875,8 +874,7 @@ Procedural type
 ~~~~~~~~~~~~~~~
 A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is
 an allowed value for variables of a procedural type. Nimrod uses procedural
-types to achieve `functional`:idx: programming techniques. Dynamic dispatch
-for OOP constructs can also be implemented with procedural types.
+types to achieve `functional`:idx: programming techniques.
 
 Example:
 
@@ -946,6 +944,16 @@ each other:
 
 Most calling conventions exist only for the Windows 32-bit platform.
 
+Assigning/passing a procedure to a procedural variable is only allowed if one 
+of the following conditions hold: 
+1) The procedure that is accessed resists in the current module.
+2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_).
+3) The procedure has a calling convention that differs from ``nimcall``. 
+4) The procedure is anonymous.
+
+These rules should prevent the case that extending a non-``procvar`` 
+procedure with default parameters breaks client code.
+
 
 Distinct type
 ~~~~~~~~~~~~~
@@ -1054,7 +1062,7 @@ describe the type checking done by the compiler.
 Type equality
 ~~~~~~~~~~~~~
 Nimrod uses structural type equivalence for most types. Only for objects,
-enumerations and abstract types name equivalence is used. The following
+enumerations and distinct types name equivalence is used. The following
 algorithm determines type equality:
 
 .. code-block:: nimrod
@@ -1800,6 +1808,77 @@ Even more elegant is to use `tuple unpacking`:idx: to access the tuple's fields:
   assert y == 3
 
 
+Multi-methods
+~~~~~~~~~~~~~
+
+Procedures always use static dispatch. Dynamic dispatch is achieved by
+`multi-methods`:idx:. 
+
+.. code-block:: nimrod
+  type
+    TExpr = object ## abstract base class for an expression
+    TLiteral = object of TExpr
+      x: int
+    TPlusExpr = object of TExpr
+      a, b: ref TExpr
+      
+  method eval(e: ref TExpr): int =
+    # override this base method
+    quit "to override!"
+  
+  method eval(e: ref TLiteral): int = return e.x
+
+  method eval(e: ref TPlusExpr): int =
+    # watch out: relies on dynamic binding
+    return eval(e.a) + eval(e.b)
+  
+  proc newLit(x: int): ref TLiteral =
+    new(result)
+    result.x = x
+    
+  proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+    new(result)
+    result.a = a
+    result.b = b
+  
+  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
+  
+In the example the constructors ``newLit`` and ``newPlus`` are procs
+because they should use static binding, but ``eval`` is a method because it
+requires dynamic binding.
+
+In a multi-method all parameters that have an object type are used for the
+dispatching:
+
+.. code-block:: nimrod
+  type
+    TThing = object
+    TUnit = object of TThing
+      x: int
+      
+  method collide(a, b: TThing) {.inline.} =
+    quit "to override!"
+    
+  method collide(a: TThing, b: TUnit) {.inline.} =
+    echo "1"
+  
+  method collide(a: TUnit, b: TThing) {.inline.} =
+    echo "2"
+  
+  var
+    a, b: TUnit
+  collide(a, b) # output: 2
+
+
+Invokation of a multi-method cannot be ambiguous: Collide 2 is prefered over 
+collide 1 because the resolution works from left to right. 
+Thus ``TUnit, TThing`` is prefered over ``TThing, TUnit``.
+
+**Perfomance note**: Nimrod does not produce a virtual method table, but
+generates dispatch trees. This avoids the expensive indirect branch for method
+calls and enables inlining. However, other optimizations like compile time
+evaluation or dead code elimination do not work with methods.
+
 
 Iterators and the for statement
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2275,6 +2354,12 @@ error to mark a proc/iterator to have no side effect if the compiler cannot
 verify this.
 
 
+procvar pragma
+--------------
+The `procvar`:idx: pragma is used to mark a proc that it can be passed to a 
+procedural variable. 
+
+
 compileTime pragma
 ------------------
 The `compileTime`:idx: pragma is used to mark a proc to be used at compile
diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt
index 26bf38a0b..f43994cb8 100755
--- a/doc/nimrodc.txt
+++ b/doc/nimrodc.txt
@@ -72,8 +72,7 @@ New Pragmas and Options
 -----------------------
 
 Because Nimrod generates C code it needs some "red tape" to work properly.
-Thus lots of options and pragmas for tweaking the generated C code are
-available.
+Lots of options and pragmas for tweaking the generated C code are available.
 
 Importc Pragma
 ~~~~~~~~~~~~~~
@@ -137,7 +136,7 @@ and instead the generated code should contain an ``#include``:
     PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
       # import C's FILE* type; Nimrod will treat it as a new pointer type
 
-The ``header`` pragma expects always a string constant. The string contant
+The ``header`` pragma always expects a string constant. The string contant
 contains the header file: As usual for C, a system header file is enclosed
 in angle brackets: ``<>``. If no angle brackets are given, Nimrod
 encloses the header file in ``""`` in the generated C code.
@@ -145,9 +144,9 @@ encloses the header file in ``""`` in the generated C code.
 
 Varargs Pragma
 ~~~~~~~~~~~~~~
-The `varargs`:idx: pragma can be applied to procedures only. It tells Nimrod
-that the proc can take a variable number of parameters after the last
-specified parameter. Nimrod string values will be converted to C
+The `varargs`:idx: pragma can be applied to procedures only (and procedure 
+types). It tells Nimrod that the proc can take a variable number of parameters 
+after the last specified parameter. Nimrod string values will be converted to C
 strings automatically:
 
 .. code-block:: Nimrod
@@ -218,7 +217,7 @@ collector to not consider objects of this type as part of a cycle:
       data: string
 
 In the example a tree structure is declared with the ``TNode`` type. Note that
-the type definition is recursive thus the GC has to assume that objects of
+the type definition is recursive and the GC has to assume that objects of
 this type may form a cyclic graph. The ``acyclic`` pragma passes the
 information that this cannot happen to the GC. If the programmer uses the
 ``acyclic`` pragma for data types that are in reality cyclic, the GC may leak
diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt
new file mode 100755
index 000000000..943acc545
--- /dev/null
+++ b/doc/pegdocs.txt
@@ -0,0 +1,180 @@
+PEG syntax and semantics
+========================
+
+A PEG (Parsing expression grammar) is a simple deterministic grammar, that can
+be directly used for parsing. The current implementation has been designed as
+a more powerful replacement for regular expressions. UTF-8 is supported.
+
+The notation used for a PEG is similar to that of EBNF:
+
+===============    ============================================================
+notation           meaning
+===============    ============================================================
+``A / ... / Z``    Ordered choice: Apply expressions `A`, ..., `Z`, in this
+                   order, to the text ahead, until one of them succeeds and
+                   possibly consumes some text. Indicate success if one of
+                   expressions succeeded. Otherwise do not consume any text
+                   and indicate failure. 
+``A ... Z``        Sequence: Apply expressions `A`, ..., `Z`, in this order,
+                   to consume consecutive portions of the text ahead, as long
+                   as they succeed. Indicate success if all succeeded.
+                   Otherwise do not consume any text and indicate failure.
+                   The sequence's precedence is higher than that of ordered
+                   choice: ``A B / C`` means ``(A B) / Z`` and
+                   not ``A (B / Z)``.
+``(E)``            Grouping: Parenthesis can be used to change
+                   operator priority.
+``{E}``            Capture: Apply expression `E` and store the substring
+                   that matched `E` into a *capture* that can be accessed
+                   after the matching process.                   
+``&E``             And predicate: Indicate success if expression `E` matches
+                   the text ahead; otherwise indicate failure. Do not consume
+                   any text.
+``!E``             Not predicate: Indicate failure if expression E matches the
+                   text ahead; otherwise indicate success. Do not consume any
+                   text.
+``E+``             One or more: Apply expression `E` repeatedly to match
+                   the text ahead, as long as it succeeds. Consume the matched
+                   text (if any) and indicate success if there was at least
+                   one match. Otherwise indicate failure. 
+``E*``             Zero or more: Apply expression `E` repeatedly to match
+                   the text ahead, as long as it succeeds. Consume the matched
+                   text (if any). Always indicate success. 
+``E?``             Zero or one: If expression `E` matches the text ahead,
+                   consume it. Always indicate success. 
+``[s]``            Character class: If the character ahead appears in the
+                   string `s`, consume it and indicate success. Otherwise
+                   indicate failure. 
+``[a-b]``          Character range: If the character ahead is one from the
+                   range `a` through `b`, consume it and indicate success.
+                   Otherwise indicate failure.
+``'s'``            String: If the text ahead is the string `s`, consume it
+                   and indicate success. Otherwise indicate failure.
+``i's'``           String match ignoring case.
+``y's'``           String match ignoring style.
+``v's'``           Verbatim string match: Use this to override a global
+                   ``\i`` or ``\y`` modifier.
+``.``              Any character: If there is a character ahead, consume it
+                   and indicate success. Otherwise (that is, at the end of
+                   input) indicate failure.
+``_``              Any Unicode character: If there is an UTF-8 character
+                   ahead, consume it and indicate success. Otherwise indicate
+                   failure.
+``A <- E``         Rule: Bind the expression `E` to the *nonterminal symbol*
+                   `A`. **Left recursive rules are not possible and crash the
+                   matching engine.**
+``\identifier``    Built-in macro for a longer expression.
+``\ddd``           Character with decimal code *ddd*.
+``\"``, etc        Literal ``"``, etc. 
+===============    ============================================================
+
+
+Built-in macros
+---------------
+
+==============     ============================================================
+macro              meaning
+==============     ============================================================
+``\d``             any decimal digit: ``[0-9]``
+``\D``             any character that is not a decimal digit: ``[^0-9]``
+``\s``             any whitespace character: ``[ \9-\13]``
+``\S``             any character that is not a whitespace character:
+                   ``[^ \9-\13]``
+``\w``             any "word" character: ``[a-zA-Z_]``
+``\W``             any "non-word" character: ``[^a-zA-Z_]``
+``\n``             any newline combination: ``\10 / \13\10 / \13``
+``\i``             ignore case for matching; use this at the start of the PEG
+``\y``             ignore style for matching; use this at the start of the PEG
+``\ident``         a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
+==============     ============================================================
+
+A backslash followed by a letter is a built-in macro, otherwise it
+is used for ordinary escaping:
+
+==============     ============================================================
+notation           meaning
+==============     ============================================================
+``\\``             a single backslash
+``\*``             same as ``'*'``
+``\t``             not a tabulator, but an (unknown) built-in
+==============     ============================================================
+
+
+Supported PEG grammar
+---------------------
+
+The PEG parser implements this grammar (written in PEG syntax)::
+
+  # Example grammar of PEG in PEG syntax.
+  # Comments start with '#'.
+  # First symbol is the start symbol.
+  
+  grammar <- rule* / expr
+  
+  identifier <- [A-Za-z][A-Za-z0-9_]*
+  charsetchar <- "\\" . / [^\]]
+  charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
+  stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
+                            "'" ("\\" . / [^'])* "'")
+  builtin <- "\\" identifier / [^\13\10]
+  
+  comment <- '#' !\n* \n
+  ig <- (\s / comment)* # things to ignore
+  
+  rule <- identifier \s* "<-" expr ig
+  identNoArrow <- identifier !(\s* "<-")
+  primary <- (ig '&' / ig '!')* ((ig identNoArrow / ig charset / ig stringlit
+                                / ig builtin / ig '.' / ig '_'
+                                / (ig "(" expr ig ")"))
+             (ig '?' / ig '*' / ig '+')*)
+  
+  # Concatenation has higher priority than choice:
+  # ``a b / c`` means ``(a b) / c``
+  
+  seqExpr <- primary+
+  expr <- seqExpr (ig "/" expr)*
+
+
+Examples
+--------
+
+Check if `s` matches Nimrod's "while" keyword:
+
+.. code-block:: nimrod
+  s =~ peg" y'while'"
+
+Exchange (key, val)-pairs: 
+
+.. code-block:: nimrod
+  "key: val; key2: val2".replace(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
+
+Determine the ``#include``'ed files of a C file:
+
+.. code-block:: nimrod
+  for line in lines("myfile.c"):
+    if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws
+                     comment <- '/*' (!'*/' . )* '*/' / '//' .*
+                     ws <- (comment / \s+)* """:    
+      echo matches[0]
+
+PEG vs regular expression
+-------------------------
+As a regular expression ``\[.*\]`` maches longest possible text between ``'['``
+and ``']'``. As a PEG it never matches anything, because a PEG is
+deterministic: ``.*`` consumes the rest of the input, so ``\]`` never matches.
+As a PEG this needs to be written as: ``\[ ( !\] . )* \]``
+
+Note that the regular expression does not behave as intended either:
+``*`` should not be greedy, so ``\[.*?\]`` should be used.
+
+
+PEG construction
+----------------
+There are two ways to construct a PEG in Nimrod code:
+(1) Parsing a string into an AST which consists of `TPeg` nodes with the
+    `peg` proc.
+(2) Constructing the AST directly with proc calls. This method does not
+    support constructing rules, only simple expressions and is not as
+    convenient. It's only advantage is that it does not pull in the whole PEG
+    parser into your executable.
+
diff --git a/doc/theindex.txt b/doc/theindex.txt
index 7eb5f84e3..922dab769 100755
--- a/doc/theindex.txt
+++ b/doc/theindex.txt
@@ -8,13 +8,12 @@ Index
 
    `!`:idx:
      * `pegs.html#114 <pegs.html#114>`_
-     * `macros.html#113 <macros.html#113>`_
+     * `macros.html#114 <macros.html#114>`_
 
    `!=`:idx:
      `system.html#347 <system.html#347>`_
 
    `$`:idx:
-     * `system.html#418 <system.html#418>`_
      * `system.html#419 <system.html#419>`_
      * `system.html#420 <system.html#420>`_
      * `system.html#421 <system.html#421>`_
@@ -22,10 +21,11 @@ Index
      * `system.html#423 <system.html#423>`_
      * `system.html#424 <system.html#424>`_
      * `system.html#425 <system.html#425>`_
+     * `system.html#426 <system.html#426>`_
      * `times.html#109 <times.html#109>`_
      * `times.html#110 <times.html#110>`_
      * `pegs.html#148 <pegs.html#148>`_
-     * `macros.html#114 <macros.html#114>`_
+     * `macros.html#115 <macros.html#115>`_
 
    `%`:idx:
      * `strutils.html#109 <strutils.html#109>`_
@@ -44,7 +44,6 @@ Index
      * `system.html#362 <system.html#362>`_
      * `system.html#363 <system.html#363>`_
      * `system.html#364 <system.html#364>`_
-     * `system.html#461 <system.html#461>`_
      * `system.html#462 <system.html#462>`_
      * `system.html#463 <system.html#463>`_
      * `system.html#464 <system.html#464>`_
@@ -182,15 +181,15 @@ Index
      `times.html#115 <times.html#115>`_
 
    `<=%`:idx:
-     `unicode.html#103 <unicode.html#103>`_
-
-   `<=%`:idx:
      * `system.html#297 <system.html#297>`_
      * `system.html#298 <system.html#298>`_
      * `system.html#299 <system.html#299>`_
      * `system.html#300 <system.html#300>`_
      * `system.html#301 <system.html#301>`_
 
+   `<=%`:idx:
+     `unicode.html#103 <unicode.html#103>`_
+
    `==`:idx:
      * `md5.html#107 <md5.html#107>`_
      * `system.html#252 <system.html#252>`_
@@ -211,25 +210,25 @@ Index
      * `system.html#465 <system.html#465>`_
      * `complex.html#102 <complex.html#102>`_
      * `unicode.html#105 <unicode.html#105>`_
-     * `macros.html#115 <macros.html#115>`_
+     * `macros.html#116 <macros.html#116>`_
 
    `=~`:idx:
-     `regexprs.html#111 <regexprs.html#111>`_
+     `pegs.html#138 <pegs.html#138>`_
 
    `=~`:idx:
-     `pegs.html#138 <pegs.html#138>`_
+     `regexprs.html#108 <regexprs.html#108>`_
 
    `>`:idx:
      `system.html#349 <system.html#349>`_
 
    `>%`:idx:
-     `system.html#417 <system.html#417>`_
+     `system.html#418 <system.html#418>`_
 
    `>=`:idx:
      `system.html#348 <system.html#348>`_
 
    `>=%`:idx:
-     `system.html#416 <system.html#416>`_
+     `system.html#417 <system.html#417>`_
 
    `?`:idx:
      `pegs.html#110 <pegs.html#110>`_
@@ -241,13 +240,13 @@ Index
      `strtabs.html#107 <strtabs.html#107>`_
 
    `[]`:idx:
-     `macros.html#111 <macros.html#111>`_
+     `macros.html#112 <macros.html#112>`_
 
    `[]=`:idx:
-     `strtabs.html#106 <strtabs.html#106>`_
+     `macros.html#113 <macros.html#113>`_
 
    `[]=`:idx:
-     `macros.html#112 <macros.html#112>`_
+     `strtabs.html#106 <strtabs.html#106>`_
 
    `[ESC]`:idx:
      `manual.html#134 <manual.html#134>`_
@@ -280,8 +279,8 @@ Index
      * `system.html#368 <system.html#368>`_
      * `system.html#369 <system.html#369>`_
      * `parsesql.html#108 <parsesql.html#108>`_
-     * `macros.html#117 <macros.html#117>`_
      * `macros.html#118 <macros.html#118>`_
+     * `macros.html#119 <macros.html#119>`_
 
    `addf`:idx:
      `strutils.html#111 <strutils.html#111>`_
@@ -301,13 +300,13 @@ Index
      `xmlgen.html#109 <xmlgen.html#109>`_
 
    `addSep`:idx:
-     `strutils.html#152 <strutils.html#152>`_
+     `strutils.html#149 <strutils.html#149>`_
 
    `alert`:idx:
      `manual.html#131 <manual.html#131>`_
 
    `allCharsInSet`:idx:
-     `strutils.html#153 <strutils.html#153>`_
+     `strutils.html#150 <strutils.html#150>`_
 
    `alloc`:idx:
      `system.html#410 <system.html#410>`_
@@ -382,7 +381,7 @@ Index
      `manual.html#199 <manual.html#199>`_
 
    `assert`:idx:
-     `system.html#414 <system.html#414>`_
+     `system.html#415 <system.html#415>`_
 
    `AST`:idx:
      `macros.html#101 <macros.html#101>`_
@@ -404,8 +403,7 @@ Index
      `xmlgen.html#111 <xmlgen.html#111>`_
 
    `backslash`:idx:
-     * `manual.html#127 <manual.html#127>`_
-     * `regexprs.html#101 <regexprs.html#101>`_
+     `manual.html#127 <manual.html#127>`_
 
    `backspace`:idx:
      `manual.html#132 <manual.html#132>`_
@@ -657,10 +655,10 @@ Index
      * `system.html#356 <system.html#356>`_
 
    `cmpIgnoreCase`:idx:
-     `strutils.html#138 <strutils.html#138>`_
+     `strutils.html#135 <strutils.html#135>`_
 
    `cmpIgnoreStyle`:idx:
-     `strutils.html#139 <strutils.html#139>`_
+     `strutils.html#136 <strutils.html#136>`_
 
    `cmpPaths`:idx:
      `os.html#139 <os.html#139>`_
@@ -701,7 +699,7 @@ Index
      `system.html#391 <system.html#391>`_
 
    `compileTime`:idx:
-     `manual.html#227 <manual.html#227>`_
+     `manual.html#229 <manual.html#229>`_
 
    `complex statements`:idx:
      `manual.html#178 <manual.html#178>`_
@@ -718,9 +716,9 @@ Index
 
    `contains`:idx:
      * `system.html#350 <system.html#350>`_
-     * `strutils.html#140 <strutils.html#140>`_
-     * `strutils.html#141 <strutils.html#141>`_
-     * `strutils.html#142 <strutils.html#142>`_
+     * `strutils.html#137 <strutils.html#137>`_
+     * `strutils.html#138 <strutils.html#138>`_
+     * `strutils.html#139 <strutils.html#139>`_
      * `pegs.html#139 <pegs.html#139>`_
      * `pegs.html#140 <pegs.html#140>`_
 
@@ -738,10 +736,10 @@ Index
      `system.html#407 <system.html#407>`_
 
    `copyNimNode`:idx:
-     `macros.html#134 <macros.html#134>`_
+     `macros.html#135 <macros.html#135>`_
 
    `copyNimTree`:idx:
-     `macros.html#135 <macros.html#135>`_
+     `macros.html#136 <macros.html#136>`_
 
    `coreAttr`:idx:
      `xmlgen.html#103 <xmlgen.html#103>`_
@@ -756,16 +754,19 @@ Index
      `math.html#109 <math.html#109>`_
 
    `countdown`:idx:
-     `system.html#435 <system.html#435>`_
+     `system.html#436 <system.html#436>`_
+
+   `countProcessors`:idx:
+     `osproc.html#117 <osproc.html#117>`_
 
    `countup`:idx:
-     `system.html#436 <system.html#436>`_
+     `system.html#437 <system.html#437>`_
 
    `cpuEndian`:idx:
      `system.html#396 <system.html#396>`_
 
    `createDir`:idx:
-     * `os.html#156 <os.html#156>`_
+     * `os.html#159 <os.html#159>`_
      * `zipfiles.html#104 <zipfiles.html#104>`_
 
    `create_random_string`:idx:
@@ -1156,7 +1157,7 @@ Index
      `manual.html#179 <manual.html#179>`_
 
    `dbgLineHook`:idx:
-     `system.html#431 <system.html#431>`_
+     `system.html#432 <system.html#432>`_
 
    `dd`:idx:
      `xmlgen.html#123 <xmlgen.html#123>`_
@@ -1184,24 +1185,24 @@ Index
 
    `del`:idx:
      * `xmlgen.html#124 <xmlgen.html#124>`_
-     * `macros.html#119 <macros.html#119>`_
+     * `macros.html#120 <macros.html#120>`_
 
    `delete`:idx:
-     `strutils.html#159 <strutils.html#159>`_
+     `strutils.html#156 <strutils.html#156>`_
 
    `deleteStr`:idx:
-     * `strutils.html#127 <strutils.html#127>`_
-     * `strutils.html#160 <strutils.html#160>`_
+     * `strutils.html#124 <strutils.html#124>`_
+     * `strutils.html#157 <strutils.html#157>`_
 
    `dfn`:idx:
      `xmlgen.html#125 <xmlgen.html#125>`_
 
-   `Digits`:idx:
-     `strutils.html#104 <strutils.html#104>`_
-
    `digits`:idx:
      `pegs.html#125 <pegs.html#125>`_
 
+   `Digits`:idx:
+     `strutils.html#104 <strutils.html#104>`_
+
    `DirSep`:idx:
      `os.html#103 <os.html#103>`_
 
@@ -1220,7 +1221,7 @@ Index
      `xmlgen.html#127 <xmlgen.html#127>`_
 
    `domain specific languages`:idx:
-     `manual.html#215 <manual.html#215>`_
+     `manual.html#216 <manual.html#216>`_
 
    `dt`:idx:
      `xmlgen.html#128 <xmlgen.html#128>`_
@@ -1265,7 +1266,7 @@ Index
      `system.html#151 <system.html#151>`_
 
    `editDistance`:idx:
-     `strutils.html#166 <strutils.html#166>`_
+     `strutils.html#163 <strutils.html#163>`_
 
    `EDivByZero`:idx:
      `system.html#147 <system.html#147>`_
@@ -1292,7 +1293,7 @@ Index
      `pegs.html#149 <pegs.html#149>`_
 
    `EInvalidRegEx`:idx:
-     `regexprs.html#104 <regexprs.html#104>`_
+     `regexprs.html#101 <regexprs.html#101>`_
 
    `EInvalidSql`:idx:
      `parsesql.html#103 <parsesql.html#103>`_
@@ -1320,7 +1321,7 @@ Index
      * `lexbase.html#101 <lexbase.html#101>`_
 
    `endsWith`:idx:
-     * `strutils.html#151 <strutils.html#151>`_
+     * `strutils.html#148 <strutils.html#148>`_
      * `pegs.html#142 <pegs.html#142>`_
 
    `ENoExceptionToReraise`:idx:
@@ -1387,9 +1388,9 @@ Index
      `system.html#145 <system.html#145>`_
 
    `error`:idx:
-     * `manual.html#225 <manual.html#225>`_
-     * `manual.html#228 <manual.html#228>`_
-     * `macros.html#136 <macros.html#136>`_
+     * `manual.html#226 <manual.html#226>`_
+     * `manual.html#230 <manual.html#230>`_
+     * `macros.html#137 <macros.html#137>`_
 
    `errorMsg`:idx:
      `parsexml.html#120 <parsexml.html#120>`_
@@ -1405,7 +1406,7 @@ Index
 
    `escape`:idx:
      * `manual.html#133 <manual.html#133>`_
-     * `strutils.html#163 <strutils.html#163>`_
+     * `strutils.html#160 <strutils.html#160>`_
 
    `escape sequences`:idx:
      `manual.html#120 <manual.html#120>`_
@@ -1435,7 +1436,7 @@ Index
      `system.html#174 <system.html#174>`_
 
    `exclFilePermissions`:idx:
-     `os.html#162 <os.html#162>`_
+     `os.html#165 <os.html#165>`_
 
    `execCmd`:idx:
      `osproc.html#105 <osproc.html#105>`_
@@ -1443,6 +1444,9 @@ Index
    `execProcess`:idx:
      `osproc.html#103 <osproc.html#103>`_
 
+   `execProcesses`:idx:
+     `osproc.html#118 <osproc.html#118>`_
+
    `execShellCmd`:idx:
      `os.html#146 <os.html#146>`_
 
@@ -1474,13 +1478,13 @@ Index
      `os.html#132 <os.html#132>`_
 
    `expectKind`:idx:
-     `macros.html#145 <macros.html#145>`_
+     `macros.html#146 <macros.html#146>`_
 
    `expectLen`:idx:
-     `macros.html#147 <macros.html#147>`_
+     `macros.html#148 <macros.html#148>`_
 
    `expectMinLen`:idx:
-     `macros.html#146 <macros.html#146>`_
+     `macros.html#147 <macros.html#147>`_
 
    `exportc`:idx:
      `nimrodc.html#102 <nimrodc.html#102>`_
@@ -1516,7 +1520,7 @@ Index
      `unicode.html#108 <unicode.html#108>`_
 
    `fatal`:idx:
-     `manual.html#229 <manual.html#229>`_
+     `manual.html#231 <manual.html#231>`_
 
    `fieldset`:idx:
      `xmlgen.html#130 <xmlgen.html#130>`_
@@ -1625,20 +1629,13 @@ Index
 
    `find`:idx:
      * `system.html#466 <system.html#466>`_
-     * `strutils.html#122 <strutils.html#122>`_
-     * `strutils.html#123 <strutils.html#123>`_
-     * `strutils.html#124 <strutils.html#124>`_
-     * `regexprs.html#109 <regexprs.html#109>`_
-     * `regexprs.html#110 <regexprs.html#110>`_
-     * `pegs.html#136 <pegs.html#136>`_
-     * `pegs.html#137 <pegs.html#137>`_
-
-   `findChars`:idx:
-     `strutils.html#121 <strutils.html#121>`_
-
-   `findSubStr`:idx:
      * `strutils.html#119 <strutils.html#119>`_
      * `strutils.html#120 <strutils.html#120>`_
+     * `strutils.html#121 <strutils.html#121>`_
+     * `regexprs.html#106 <regexprs.html#106>`_
+     * `regexprs.html#107 <regexprs.html#107>`_
+     * `pegs.html#136 <pegs.html#136>`_
+     * `pegs.html#137 <pegs.html#137>`_
 
    `float`:idx:
      `system.html#106 <system.html#106>`_
@@ -1650,16 +1647,16 @@ Index
      `system.html#108 <system.html#108>`_
 
    `floatVal`:idx:
-     `macros.html#122 <macros.html#122>`_
+     `macros.html#123 <macros.html#123>`_
 
    `floatVal=`:idx:
-     `macros.html#128 <macros.html#128>`_
+     `macros.html#129 <macros.html#129>`_
 
    `FlushFile`:idx:
      `system.html#503 <system.html#503>`_
 
    `for`:idx:
-     * `manual.html#207 <manual.html#207>`_
+     * `manual.html#208 <manual.html#208>`_
      * `tut1.html#105 <tut1.html#105>`_
 
    `form`:idx:
@@ -1718,18 +1715,15 @@ Index
    `generalized raw string literal`:idx:
      `manual.html#136 <manual.html#136>`_
 
-   `generic character types`:idx:
-     `regexprs.html#102 <regexprs.html#102>`_
-
    `Generics`:idx:
-     * `manual.html#211 <manual.html#211>`_
+     * `manual.html#212 <manual.html#212>`_
      * `tut2.html#109 <tut2.html#109>`_
 
    `getApplicationDir`:idx:
-     `os.html#166 <os.html#166>`_
+     `os.html#169 <os.html#169>`_
 
    `getApplicationFilename`:idx:
-     `os.html#165 <os.html#165>`_
+     `os.html#168 <os.html#168>`_
 
    `getClockStr`:idx:
      `times.html#112 <times.html#112>`_
@@ -1742,7 +1736,7 @@ Index
      * `parsexml.html#117 <parsexml.html#117>`_
 
    `getConfigDir`:idx:
-     `os.html#164 <os.html#164>`_
+     `os.html#167 <os.html#167>`_
 
    `getContentLength`:idx:
      `cgi.html#110 <cgi.html#110>`_
@@ -1757,7 +1751,7 @@ Index
      `os.html#120 <os.html#120>`_
 
    `getCurrentExceptionMsg`:idx:
-     `system.html#427 <system.html#427>`_
+     `system.html#428 <system.html#428>`_
 
    `getCurrentLine`:idx:
      `lexbase.html#106 <lexbase.html#106>`_
@@ -1776,7 +1770,7 @@ Index
      * `parsexml.html#119 <parsexml.html#119>`_
 
    `getFilePermissions`:idx:
-     `os.html#159 <os.html#159>`_
+     `os.html#162 <os.html#162>`_
 
    `getFilePos`:idx:
      `system.html#523 <system.html#523>`_
@@ -1785,7 +1779,7 @@ Index
      `system.html#515 <system.html#515>`_
 
    `getFreeMem`:idx:
-     `system.html#433 <system.html#433>`_
+     `system.html#434 <system.html#434>`_
 
    `getGatewayInterface`:idx:
      `cgi.html#113 <cgi.html#113>`_
@@ -1794,7 +1788,7 @@ Index
      `times.html#107 <times.html#107>`_
 
    `getHomeDir`:idx:
-     `os.html#163 <os.html#163>`_
+     `os.html#166 <os.html#166>`_
 
    `getHttpAccept`:idx:
      `cgi.html#114 <cgi.html#114>`_
@@ -1840,7 +1834,7 @@ Index
      `md5.html#106 <md5.html#106>`_
 
    `getOccupiedMem`:idx:
-     `system.html#432 <system.html#432>`_
+     `system.html#433 <system.html#433>`_
 
    `getopt`:idx:
      `parseopt.html#108 <parseopt.html#108>`_
@@ -1855,7 +1849,7 @@ Index
      `cgi.html#125 <cgi.html#125>`_
 
    `getRefcount`:idx:
-     `system.html#426 <system.html#426>`_
+     `system.html#427 <system.html#427>`_
 
    `getRemoteAddr`:idx:
      `cgi.html#126 <cgi.html#126>`_
@@ -1924,7 +1918,7 @@ Index
      `times.html#105 <times.html#105>`_
 
    `getTotalMem`:idx:
-     `system.html#434 <system.html#434>`_
+     `system.html#435 <system.html#435>`_
 
    `get_tty_password`:idx:
      `mysql.html#282 <mysql.html#282>`_
@@ -1991,9 +1985,9 @@ Index
      `system.html#126 <system.html#126>`_
 
    `hint`:idx:
-     * `manual.html#223 <manual.html#223>`_
-     * `manual.html#231 <manual.html#231>`_
-     * `macros.html#138 <macros.html#138>`_
+     * `manual.html#224 <manual.html#224>`_
+     * `manual.html#233 <manual.html#233>`_
+     * `macros.html#139 <macros.html#139>`_
 
    `hostCPU`:idx:
      `system.html#398 <system.html#398>`_
@@ -2040,10 +2034,10 @@ Index
 
    `ident`:idx:
      * `pegs.html#129 <pegs.html#129>`_
-     * `macros.html#124 <macros.html#124>`_
+     * `macros.html#125 <macros.html#125>`_
 
    `ident=`:idx:
-     `macros.html#130 <macros.html#130>`_
+     `macros.html#131 <macros.html#131>`_
 
    `identChars`:idx:
      `pegs.html#127 <pegs.html#127>`_
@@ -2057,12 +2051,12 @@ Index
    `Identifiers`:idx:
      `manual.html#116 <manual.html#116>`_
 
-   `IdentStartChars`:idx:
-     `strutils.html#106 <strutils.html#106>`_
-
    `identStartChars`:idx:
      `pegs.html#128 <pegs.html#128>`_
 
+   `IdentStartChars`:idx:
+     `strutils.html#106 <strutils.html#106>`_
+
    `if`:idx:
      `manual.html#183 <manual.html#183>`_
 
@@ -2070,10 +2064,10 @@ Index
      `xmlgen.html#142 <xmlgen.html#142>`_
 
    `implicit block`:idx:
-     `manual.html#209 <manual.html#209>`_
+     `manual.html#210 <manual.html#210>`_
 
    `import`:idx:
-     * `manual.html#219 <manual.html#219>`_
+     * `manual.html#220 <manual.html#220>`_
      * `tut1.html#128 <tut1.html#128>`_
 
    `importc`:idx:
@@ -2089,7 +2083,7 @@ Index
      `system.html#173 <system.html#173>`_
 
    `inclFilePermissions`:idx:
-     `os.html#161 <os.html#161>`_
+     `os.html#164 <os.html#164>`_
 
    `include`:idx:
      `tut1.html#129 <tut1.html#129>`_
@@ -2098,10 +2092,10 @@ Index
      `manual.html#113 <manual.html#113>`_
 
    `inf`:idx:
-     `system.html#428 <system.html#428>`_
+     `system.html#429 <system.html#429>`_
 
    `information hiding`:idx:
-     * `manual.html#217 <manual.html#217>`_
+     * `manual.html#218 <manual.html#218>`_
      * `tut1.html#126 <tut1.html#126>`_
 
    `init`:idx:
@@ -2141,13 +2135,13 @@ Index
      `mysql.html#306 <mysql.html#306>`_
 
    `intToStr`:idx:
-     `strutils.html#144 <strutils.html#144>`_
+     `strutils.html#141 <strutils.html#141>`_
 
    `intVal`:idx:
-     `macros.html#121 <macros.html#121>`_
+     `macros.html#122 <macros.html#122>`_
 
    `intVal=`:idx:
-     `macros.html#127 <macros.html#127>`_
+     `macros.html#128 <macros.html#128>`_
 
    `is`:idx:
      `system.html#353 <system.html#353>`_
@@ -2165,12 +2159,12 @@ Index
      `system.html#389 <system.html#389>`_
 
    `isNil`:idx:
-     * `system.html#455 <system.html#455>`_
      * `system.html#456 <system.html#456>`_
      * `system.html#457 <system.html#457>`_
      * `system.html#458 <system.html#458>`_
      * `system.html#459 <system.html#459>`_
      * `system.html#460 <system.html#460>`_
+     * `system.html#461 <system.html#461>`_
 
    `is_not`:idx:
      `system.html#354 <system.html#354>`_
@@ -2203,22 +2197,22 @@ Index
      `mysql.html#255 <mysql.html#255>`_
 
    `items`:idx:
-     * `system.html#449 <system.html#449>`_
      * `system.html#450 <system.html#450>`_
      * `system.html#451 <system.html#451>`_
      * `system.html#452 <system.html#452>`_
      * `system.html#453 <system.html#453>`_
      * `system.html#454 <system.html#454>`_
+     * `system.html#455 <system.html#455>`_
 
    `iterator`:idx:
-     `manual.html#208 <manual.html#208>`_
+     `manual.html#209 <manual.html#209>`_
 
    `iterOverEnvironment`:idx:
      `os.html#150 <os.html#150>`_
 
    `join`:idx:
-     * `strutils.html#155 <strutils.html#155>`_
-     * `strutils.html#156 <strutils.html#156>`_
+     * `strutils.html#152 <strutils.html#152>`_
+     * `strutils.html#153 <strutils.html#153>`_
 
    `JoinPath`:idx:
      * `os.html#122 <os.html#122>`_
@@ -2232,7 +2226,7 @@ Index
 
    `kind`:idx:
      * `parsexml.html#110 <parsexml.html#110>`_
-     * `macros.html#120 <macros.html#120>`_
+     * `macros.html#121 <macros.html#121>`_
 
    `l-values`:idx:
      `manual.html#107 <manual.html#107>`_
@@ -2251,7 +2245,7 @@ Index
      * `system.html#172 <system.html#172>`_
      * `strtabs.html#109 <strtabs.html#109>`_
      * `parsesql.html#107 <parsesql.html#107>`_
-     * `macros.html#116 <macros.html#116>`_
+     * `macros.html#117 <macros.html#117>`_
 
    `Letters`:idx:
      `strutils.html#103 <strutils.html#103>`_
@@ -2326,7 +2320,7 @@ Index
      `system.html#127 <system.html#127>`_
 
    `Macros`:idx:
-     `manual.html#214 <manual.html#214>`_
+     `manual.html#215 <manual.html#215>`_
 
    `make_password_from_salt`:idx:
      `mysql.html#281 <mysql.html#281>`_
@@ -2359,24 +2353,24 @@ Index
      `xmlgen.html#150 <xmlgen.html#150>`_
 
    `match`:idx:
-     * `regexprs.html#106 <regexprs.html#106>`_
-     * `regexprs.html#107 <regexprs.html#107>`_
+     * `regexprs.html#103 <regexprs.html#103>`_
+     * `regexprs.html#104 <regexprs.html#104>`_
      * `pegs.html#132 <pegs.html#132>`_
      * `pegs.html#133 <pegs.html#133>`_
 
    `matchLen`:idx:
-     * `regexprs.html#108 <regexprs.html#108>`_
+     * `regexprs.html#105 <regexprs.html#105>`_
      * `pegs.html#134 <pegs.html#134>`_
      * `pegs.html#135 <pegs.html#135>`_
 
    `max`:idx:
      * `system.html#318 <system.html#318>`_
-     * `system.html#443 <system.html#443>`_
      * `system.html#444 <system.html#444>`_
      * `system.html#445 <system.html#445>`_
      * `system.html#446 <system.html#446>`_
      * `system.html#447 <system.html#447>`_
      * `system.html#448 <system.html#448>`_
+     * `system.html#449 <system.html#449>`_
 
    `MAX_BIGINT_WIDTH`:idx:
      `mysql.html#194 <mysql.html#194>`_
@@ -2403,7 +2397,7 @@ Index
      `mysql.html#191 <mysql.html#191>`_
 
    `MaxSubpatterns`:idx:
-     * `regexprs.html#105 <regexprs.html#105>`_
+     * `regexprs.html#102 <regexprs.html#102>`_
      * `pegs.html#131 <pegs.html#131>`_
 
    `MAX_TINYINT_WIDTH`:idx:
@@ -2441,12 +2435,12 @@ Index
 
    `min`:idx:
      * `system.html#317 <system.html#317>`_
-     * `system.html#437 <system.html#437>`_
      * `system.html#438 <system.html#438>`_
      * `system.html#439 <system.html#439>`_
      * `system.html#440 <system.html#440>`_
      * `system.html#441 <system.html#441>`_
      * `system.html#442 <system.html#442>`_
+     * `system.html#443 <system.html#443>`_
 
    `mod`:idx:
      * `system.html#222 <system.html#222>`_
@@ -2459,7 +2453,7 @@ Index
      `mysql.html#284 <mysql.html#284>`_
 
    `module`:idx:
-     * `manual.html#216 <manual.html#216>`_
+     * `manual.html#217 <manual.html#217>`_
      * `tut1.html#125 <tut1.html#125>`_
 
    `moveFile`:idx:
@@ -2469,7 +2463,8 @@ Index
      `system.html#408 <system.html#408>`_
 
    `multi-methods`:idx:
-     `tut2.html#104 <tut2.html#104>`_
+     * `manual.html#207 <manual.html#207>`_
+     * `tut2.html#104 <tut2.html#104>`_
 
    `MULTIPLE_KEY_FLAG`:idx:
      `mysql.html#127 <mysql.html#127>`_
@@ -2989,7 +2984,7 @@ Index
      `mysql.html#110 <mysql.html#110>`_
 
    `nan`:idx:
-     `system.html#430 <system.html#430>`_
+     `system.html#431 <system.html#431>`_
 
    `Natural`:idx:
      `system.html#134 <system.html#134>`_
@@ -2998,10 +2993,10 @@ Index
      `pegs.html#130 <pegs.html#130>`_
 
    `neginf`:idx:
-     `system.html#429 <system.html#429>`_
+     `system.html#430 <system.html#430>`_
 
    `nestList`:idx:
-     `macros.html#150 <macros.html#150>`_
+     `macros.html#151 <macros.html#151>`_
 
    `NET`:idx:
      `mysql.html#199 <mysql.html#199>`_
@@ -3047,35 +3042,35 @@ Index
      * `system.html#125 <system.html#125>`_
 
    `newCall`:idx:
-     * `macros.html#148 <macros.html#148>`_
      * `macros.html#149 <macros.html#149>`_
+     * `macros.html#150 <macros.html#150>`_
 
    `newFileStream`:idx:
      * `streams.html#120 <streams.html#120>`_
      * `streams.html#121 <streams.html#121>`_
 
    `newFloatLitNode`:idx:
-     `macros.html#141 <macros.html#141>`_
+     `macros.html#142 <macros.html#142>`_
 
    `newIdentNode`:idx:
-     * `macros.html#142 <macros.html#142>`_
      * `macros.html#143 <macros.html#143>`_
+     * `macros.html#144 <macros.html#144>`_
 
    `newIntLitNode`:idx:
-     `macros.html#140 <macros.html#140>`_
-
-   `newLine`:idx:
-     `pegs.html#120 <pegs.html#120>`_
+     `macros.html#141 <macros.html#141>`_
 
    `newline`:idx:
      * `manual.html#121 <manual.html#121>`_
      * `pegs.html#119 <pegs.html#119>`_
 
+   `newLine`:idx:
+     `pegs.html#120 <pegs.html#120>`_
+
    `NewLines`:idx:
      `lexbase.html#102 <lexbase.html#102>`_
 
    `newNimNode`:idx:
-     `macros.html#133 <macros.html#133>`_
+     `macros.html#134 <macros.html#134>`_
 
    `newNonTerminal`:idx:
      `pegs.html#123 <pegs.html#123>`_
@@ -3094,7 +3089,7 @@ Index
      * `strtabs.html#105 <strtabs.html#105>`_
 
    `newStrLitNode`:idx:
-     `macros.html#139 <macros.html#139>`_
+     `macros.html#140 <macros.html#140>`_
 
    `next`:idx:
      * `parseopt.html#105 <parseopt.html#105>`_
@@ -3138,7 +3133,7 @@ Index
      `xmlgen.html#152 <xmlgen.html#152>`_
 
    `noSideEffect`:idx:
-     `manual.html#226 <manual.html#226>`_
+     `manual.html#227 <manual.html#227>`_
 
    `not`:idx:
      * `system.html#120 <system.html#120>`_
@@ -3261,16 +3256,16 @@ Index
      `os.html#127 <os.html#127>`_
 
    `ParseBiggestInt`:idx:
-     `strutils.html#146 <strutils.html#146>`_
+     `strutils.html#143 <strutils.html#143>`_
 
    `parseCmdLine`:idx:
-     `os.html#157 <os.html#157>`_
+     `os.html#160 <os.html#160>`_
 
    `ParseFloat`:idx:
-     `strutils.html#147 <strutils.html#147>`_
+     `strutils.html#144 <strutils.html#144>`_
 
    `ParseInt`:idx:
-     `strutils.html#145 <strutils.html#145>`_
+     `strutils.html#142 <strutils.html#142>`_
 
    `parsePeg`:idx:
      `pegs.html#150 <pegs.html#150>`_
@@ -3284,12 +3279,18 @@ Index
    `PathSep`:idx:
      `os.html#105 <os.html#105>`_
 
+   `pcDirectory`:idx:
+     `os.html#154 <os.html#154>`_
+
    `Pcharacter_set`:idx:
      `mysql.html#350 <mysql.html#350>`_
 
    `Pcharset_info_st`:idx:
      `mysql.html#349 <mysql.html#349>`_
 
+   `pcLinkToDirectory`:idx:
+     `os.html#155 <os.html#155>`_
+
    `PCURL`:idx:
      `libcurl.html#139 <libcurl.html#139>`_
 
@@ -3495,13 +3496,13 @@ Index
      `mysql.html#200 <mysql.html#200>`_
 
    `PNimrodNode`:idx:
-     `macros.html#110 <macros.html#110>`_
+     `macros.html#111 <macros.html#111>`_
 
    `PNimrodSymbol`:idx:
-     `macros.html#109 <macros.html#109>`_
+     `macros.html#110 <macros.html#110>`_
 
    `PNimrodType`:idx:
-     `macros.html#108 <macros.html#108>`_
+     `macros.html#109 <macros.html#109>`_
 
    `PNonTerminal`:idx:
      `pegs.html#101 <pegs.html#101>`_
@@ -3571,6 +3572,12 @@ Index
    `processID`:idx:
      `osproc.html#112 <osproc.html#112>`_
 
+   `procvar`:idx:
+     `manual.html#228 <manual.html#228>`_
+
+   `programming by contracts`:idx:
+     `system.html#414 <system.html#414>`_
+
    `Psockaddr`:idx:
      `mysql.html#250 <mysql.html#250>`_
 
@@ -3662,7 +3669,7 @@ Index
      `math.html#134 <math.html#134>`_
 
    `push/pop`:idx:
-     `manual.html#232 <manual.html#232>`_
+     `manual.html#234 <manual.html#234>`_
 
    `putEnv`:idx:
      `os.html#149 <os.html#149>`_
@@ -3690,7 +3697,7 @@ Index
      `manual.html#128 <manual.html#128>`_
 
    `quoteIfContainsWhite`:idx:
-     `strutils.html#154 <strutils.html#154>`_
+     `strutils.html#151 <strutils.html#151>`_
 
    `random`:idx:
      `math.html#113 <math.html#113>`_
@@ -3764,16 +3771,16 @@ Index
      `system.html#412 <system.html#412>`_
 
    `reBinary`:idx:
-     `regexprs.html#116 <regexprs.html#116>`_
+     `regexprs.html#113 <regexprs.html#113>`_
 
    `Recursive module dependancies`:idx:
-     `manual.html#220 <manual.html#220>`_
+     `manual.html#221 <manual.html#221>`_
 
    `reEmail`:idx:
-     `regexprs.html#119 <regexprs.html#119>`_
+     `regexprs.html#116 <regexprs.html#116>`_
 
    `reFloat`:idx:
-     `regexprs.html#118 <regexprs.html#118>`_
+     `regexprs.html#115 <regexprs.html#115>`_
 
    `REFRESH_DES_KEY_FILE`:idx:
      `mysql.html#154 <mysql.html#154>`_
@@ -3821,40 +3828,40 @@ Index
      `nimrodc.html#112 <nimrodc.html#112>`_
 
    `reHex`:idx:
-     `regexprs.html#115 <regexprs.html#115>`_
+     `regexprs.html#112 <regexprs.html#112>`_
 
    `reIdentifier`:idx:
-     `regexprs.html#112 <regexprs.html#112>`_
+     `regexprs.html#109 <regexprs.html#109>`_
 
    `reInteger`:idx:
-     `regexprs.html#114 <regexprs.html#114>`_
+     `regexprs.html#111 <regexprs.html#111>`_
 
    `removeDir`:idx:
-     `os.html#155 <os.html#155>`_
+     `os.html#158 <os.html#158>`_
 
    `removeFile`:idx:
      `os.html#144 <os.html#144>`_
 
    `reNatural`:idx:
-     `regexprs.html#113 <regexprs.html#113>`_
+     `regexprs.html#110 <regexprs.html#110>`_
 
    `renderSQL`:idx:
      `parsesql.html#110 <parsesql.html#110>`_
 
    `reOctal`:idx:
-     `regexprs.html#117 <regexprs.html#117>`_
+     `regexprs.html#114 <regexprs.html#114>`_
 
    `repeatChar`:idx:
-     `strutils.html#149 <strutils.html#149>`_
+     `strutils.html#146 <strutils.html#146>`_
 
    `replace`:idx:
-     * `strutils.html#157 <strutils.html#157>`_
-     * `strutils.html#158 <strutils.html#158>`_
+     * `strutils.html#154 <strutils.html#154>`_
+     * `strutils.html#155 <strutils.html#155>`_
      * `pegs.html#143 <pegs.html#143>`_
 
    `replaceStr`:idx:
-     * `strutils.html#125 <strutils.html#125>`_
-     * `strutils.html#126 <strutils.html#126>`_
+     * `strutils.html#122 <strutils.html#122>`_
+     * `strutils.html#123 <strutils.html#123>`_
 
    `repr`:idx:
      `system.html#370 <system.html#370>`_
@@ -3873,7 +3880,7 @@ Index
      `manual.html#192 <manual.html#192>`_
 
    `reURL`:idx:
-     `regexprs.html#120 <regexprs.html#120>`_
+     `regexprs.html#117 <regexprs.html#117>`_
 
    `round`:idx:
      `math.html#121 <math.html#121>`_
@@ -3910,7 +3917,7 @@ Index
 
    `scope`:idx:
      * `manual.html#106 <manual.html#106>`_
-     * `manual.html#221 <manual.html#221>`_
+     * `manual.html#222 <manual.html#222>`_
 
    `scramble`:idx:
      `mysql.html#278 <mysql.html#278>`_
@@ -3940,7 +3947,7 @@ Index
      `xmlgen.html#163 <xmlgen.html#163>`_
 
    `separate compilation`:idx:
-     * `manual.html#218 <manual.html#218>`_
+     * `manual.html#219 <manual.html#219>`_
      * `tut1.html#127 <tut1.html#127>`_
 
    `seq`:idx:
@@ -4009,7 +4016,7 @@ Index
      `terminal.html#103 <terminal.html#103>`_
 
    `setFilePermissions`:idx:
-     `os.html#160 <os.html#160>`_
+     `os.html#163 <os.html#163>`_
 
    `setFilePos`:idx:
      `system.html#522 <system.html#522>`_
@@ -4041,9 +4048,6 @@ Index
      * `system.html#230 <system.html#230>`_
      * `system.html#231 <system.html#231>`_
 
-   `simple assertions`:idx:
-     `regexprs.html#103 <regexprs.html#103>`_
-
    `simple statements`:idx:
      `manual.html#177 <manual.html#177>`_
 
@@ -4053,6 +4057,9 @@ Index
    `sizeof`:idx:
      `system.html#162 <system.html#162>`_
 
+   `sleep`:idx:
+     `os.html#170 <os.html#170>`_
+
    `small`:idx:
      `xmlgen.html#164 <xmlgen.html#164>`_
 
@@ -4063,10 +4070,10 @@ Index
      `xmlgen.html#165 <xmlgen.html#165>`_
 
    `split`:idx:
-     * `strutils.html#129 <strutils.html#129>`_
-     * `strutils.html#130 <strutils.html#130>`_
-     * `strutils.html#136 <strutils.html#136>`_
-     * `strutils.html#137 <strutils.html#137>`_
+     * `strutils.html#126 <strutils.html#126>`_
+     * `strutils.html#127 <strutils.html#127>`_
+     * `strutils.html#133 <strutils.html#133>`_
+     * `strutils.html#134 <strutils.html#134>`_
      * `pegs.html#146 <pegs.html#146>`_
      * `pegs.html#147 <pegs.html#147>`_
 
@@ -4077,19 +4084,19 @@ Index
      `os.html#133 <os.html#133>`_
 
    `splitLines`:idx:
-     * `strutils.html#131 <strutils.html#131>`_
-     * `strutils.html#135 <strutils.html#135>`_
+     * `strutils.html#128 <strutils.html#128>`_
+     * `strutils.html#132 <strutils.html#132>`_
 
    `splitLinesSeq`:idx:
-     `strutils.html#132 <strutils.html#132>`_
+     `strutils.html#129 <strutils.html#129>`_
 
    `SplitPath`:idx:
      * `os.html#125 <os.html#125>`_
      * `os.html#126 <os.html#126>`_
 
    `splitSeq`:idx:
-     * `strutils.html#133 <strutils.html#133>`_
-     * `strutils.html#134 <strutils.html#134>`_
+     * `strutils.html#130 <strutils.html#130>`_
+     * `strutils.html#131 <strutils.html#131>`_
 
    `sqlite3_aggregate_context`:idx:
      `sqlite3.html#261 <sqlite3.html#261>`_
@@ -4600,11 +4607,10 @@ Index
      `math.html#136 <math.html#136>`_
 
    `startProcess`:idx:
-     * `osproc.html#107 <osproc.html#107>`_
-     * `osproc.html#117 <osproc.html#117>`_
+     `osproc.html#107 <osproc.html#107>`_
 
    `startsWith`:idx:
-     * `strutils.html#150 <strutils.html#150>`_
+     * `strutils.html#147 <strutils.html#147>`_
      * `pegs.html#141 <pegs.html#141>`_
 
    `statement macros`:idx:
@@ -4696,10 +4702,10 @@ Index
      `manual.html#152 <manual.html#152>`_
 
    `strVal`:idx:
-     `macros.html#126 <macros.html#126>`_
+     `macros.html#127 <macros.html#127>`_
 
    `strVal=`:idx:
-     `macros.html#132 <macros.html#132>`_
+     `macros.html#133 <macros.html#133>`_
 
    `st_udf_args`:idx:
      `mysql.html#258 <mysql.html#258>`_
@@ -4739,22 +4745,22 @@ Index
      `osproc.html#108 <osproc.html#108>`_
 
    `swap`:idx:
-     `system.html#415 <system.html#415>`_
+     `system.html#416 <system.html#416>`_
 
    `symAddr`:idx:
      `dynlib.html#104 <dynlib.html#104>`_
 
    `symbol`:idx:
-     `macros.html#123 <macros.html#123>`_
+     `macros.html#124 <macros.html#124>`_
 
    `symbol=`:idx:
-     `macros.html#129 <macros.html#129>`_
+     `macros.html#130 <macros.html#130>`_
 
    `syscall`:idx:
      `manual.html#172 <manual.html#172>`_
 
    `system`:idx:
-     `manual.html#222 <manual.html#222>`_
+     `manual.html#223 <manual.html#223>`_
 
    `table`:idx:
      `xmlgen.html#170 <xmlgen.html#170>`_
@@ -4973,7 +4979,7 @@ Index
      `xmlgen.html#172 <xmlgen.html#172>`_
 
    `template`:idx:
-     `manual.html#213 <manual.html#213>`_
+     `manual.html#214 <manual.html#214>`_
 
    `TEndian`:idx:
      `system.html#384 <system.html#384>`_
@@ -5004,7 +5010,7 @@ Index
      `system.html#490 <system.html#490>`_
 
    `TFilePermission`:idx:
-     `os.html#158 <os.html#158>`_
+     `os.html#161 <os.html#161>`_
 
    `TFileStream`:idx:
      `streams.html#119 <streams.html#119>`_
@@ -5051,6 +5057,9 @@ Index
    `TNimNodeKinds`:idx:
      `macros.html#103 <macros.html#103>`_
 
+   `TNimrodIdent`:idx:
+     `macros.html#108 <macros.html#108>`_
+
    `TNimrodNodeKind`:idx:
      `macros.html#102 <macros.html#102>`_
 
@@ -5073,7 +5082,7 @@ Index
      `system.html#402 <system.html#402>`_
 
    `toBin`:idx:
-     `strutils.html#162 <strutils.html#162>`_
+     `strutils.html#159 <strutils.html#159>`_
 
    `TObject`:idx:
      `system.html#136 <system.html#136>`_
@@ -5082,7 +5091,7 @@ Index
      `system.html#399 <system.html#399>`_
 
    `toHex`:idx:
-     `strutils.html#143 <strutils.html#143>`_
+     `strutils.html#140 <strutils.html#140>`_
 
    `toInt`:idx:
      `system.html#401 <system.html#401>`_
@@ -5093,19 +5102,19 @@ Index
      * `unicode.html#111 <unicode.html#111>`_
 
    `toOct`:idx:
-     `strutils.html#161 <strutils.html#161>`_
+     `strutils.html#158 <strutils.html#158>`_
 
    `toOctal`:idx:
-     `strutils.html#128 <strutils.html#128>`_
+     `strutils.html#125 <strutils.html#125>`_
 
    `TOptParser`:idx:
      `parseopt.html#102 <parseopt.html#102>`_
 
    `toString`:idx:
-     `strutils.html#148 <strutils.html#148>`_
+     `strutils.html#145 <strutils.html#145>`_
 
    `toStrLit`:idx:
-     `macros.html#144 <macros.html#144>`_
+     `macros.html#145 <macros.html#145>`_
 
    `toTitle`:idx:
      `unicode.html#113 <unicode.html#113>`_
@@ -5235,15 +5244,15 @@ Index
      `parsexml.html#106 <parsexml.html#106>`_
 
    `typ`:idx:
-     `macros.html#125 <macros.html#125>`_
+     `macros.html#126 <macros.html#126>`_
 
    `typ=`:idx:
-     `macros.html#131 <macros.html#131>`_
+     `macros.html#132 <macros.html#132>`_
 
    `type`:idx:
      * `manual.html#102 <manual.html#102>`_
      * `manual.html#141 <manual.html#141>`_
-     * `manual.html#210 <manual.html#210>`_
+     * `manual.html#211 <manual.html#211>`_
 
    `type casts`:idx:
      `tut2.html#101 <tut2.html#101>`_
@@ -5252,7 +5261,7 @@ Index
      `tut2.html#102 <tut2.html#102>`_
 
    `type parameters`:idx:
-     * `manual.html#212 <manual.html#212>`_
+     * `manual.html#213 <manual.html#213>`_
      * `tut2.html#110 <tut2.html#110>`_
 
    `type suffix`:idx:
@@ -5331,17 +5340,17 @@ Index
      `cgi.html#109 <cgi.html#109>`_
 
    `validEmailAddress`:idx:
-     `strutils.html#164 <strutils.html#164>`_
+     `strutils.html#161 <strutils.html#161>`_
 
    `validIdentifier`:idx:
-     `strutils.html#165 <strutils.html#165>`_
-
-   `var`:idx:
-     `xmlgen.html#181 <xmlgen.html#181>`_
+     `strutils.html#162 <strutils.html#162>`_
 
    `Var`:idx:
      `manual.html#181 <manual.html#181>`_
 
+   `var`:idx:
+     `xmlgen.html#181 <xmlgen.html#181>`_
+
    `varargs`:idx:
      `nimrodc.html#106 <nimrodc.html#106>`_
 
@@ -5354,7 +5363,7 @@ Index
      * `tut2.html#103 <tut2.html#103>`_
 
    `verbose`:idx:
-     `regexprs.html#121 <regexprs.html#121>`_
+     `regexprs.html#118 <regexprs.html#118>`_
 
    `vertical tabulator`:idx:
      `manual.html#126 <manual.html#126>`_
@@ -5366,16 +5375,19 @@ Index
      `osproc.html#113 <osproc.html#113>`_
 
    `walkDir`:idx:
-     `os.html#154 <os.html#154>`_
+     `os.html#156 <os.html#156>`_
+
+   `walkDirRec`:idx:
+     `os.html#157 <os.html#157>`_
 
    `walkFiles`:idx:
      * `os.html#152 <os.html#152>`_
      * `zipfiles.html#110 <zipfiles.html#110>`_
 
    `warning`:idx:
-     * `manual.html#224 <manual.html#224>`_
-     * `manual.html#230 <manual.html#230>`_
-     * `macros.html#137 <macros.html#137>`_
+     * `manual.html#225 <manual.html#225>`_
+     * `manual.html#232 <manual.html#232>`_
+     * `macros.html#138 <macros.html#138>`_
 
    `when`:idx:
      * `manual.html#185 <manual.html#185>`_
@@ -5384,12 +5396,12 @@ Index
    `while`:idx:
      `manual.html#197 <manual.html#197>`_
 
-   `whitespace`:idx:
-     `pegs.html#126 <pegs.html#126>`_
-
    `Whitespace`:idx:
      `strutils.html#102 <strutils.html#102>`_
 
+   `whitespace`:idx:
+     `pegs.html#126 <pegs.html#126>`_
+
    `winTimeToUnixTime`:idx:
      `times.html#118 <times.html#118>`_
 
diff --git a/doc/tut1.txt b/doc/tut1.txt
index 69f218a31..c3d7b0039 100755
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -1,4 +1,4 @@
-========================
+========================
 Nimrod Tutorial (Part I)
 ========================
 
@@ -10,7 +10,7 @@ Nimrod Tutorial (Part I)
 Introduction
 ============
 
-  "Before you run you must learn to walk."
+  "Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir."
 
 This document is a tutorial for the programming language *Nimrod*. After this
 tutorial you will have a decent knowledge about Nimrod. This tutorial assumes
@@ -34,8 +34,8 @@ Save this code to the file "greetings.nim". Now compile and run it::
 
   nimrod compile --run greetings.nim
 
-As you see, with the ``--run`` switch Nimrod executes the file automatically
-after compilation. You can even give your program command line arguments by
+With the ``--run`` switch Nimrod executes the file automatically
+after compilation. You can give your program command line arguments by
 appending them after the filename::
 
   nimrod compile --run greetings.nim arg1 arg2
diff --git a/doc/tut2.txt b/doc/tut2.txt
index 4515ffbd5..c81f98e77 100755
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -177,8 +177,7 @@ bound to a class. This has disadvantages:
 * Adding a method to a class the programmer has no control over is 
   impossible or needs ugly workarounds.
 * Often it is unclear where the method should belong to: Is
-  ``join`` a string method or an array method? Should the complex 
-  ``vertexCover`` algorithm really be a method of the ``graph`` class?
+  ``join`` a string method or an array method?
 
 Nimrod avoids these problems by not assigning methods to a class. All methods
 in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
@@ -206,7 +205,7 @@ for any type:
 (Another way to look at the method call syntax is that it provides the missing
 postfix notation.)
 
-So code that looks "pure object oriented" is easy to write:
+So "pure object oriented" code is easy to write:
 
 .. code-block:: nimrod
   import strutils
@@ -277,7 +276,7 @@ already provides ``v[]`` access.
 Dynamic dispatch
 ----------------
 
-Procedures always use static dispatch. To get dynamic dispatch, replace the
+Procedures always use static dispatch. For dynamic dispatch replace the
 ``proc`` keyword by ``method``:
 
 .. code-block:: nimrod
diff --git a/examples/filterex.nim b/examples/filterex.nim
new file mode 100755
index 000000000..3713f4b64
--- /dev/null
+++ b/examples/filterex.nim
@@ -0,0 +1,23 @@
+#! stdtmpl | standard
+#proc generateHTMLPage(title, currentTab, content: string,
+#                      tabs: openArray[string]): string = 
+#  result = ""
+<head><title>$title</title></head>
+<body>
+  <div id="menu">
+    <ul>
+  #for tab in items(tabs):
+    #if currentTab == tab:
+    <li><a id="selected" 
+    #else:
+    <li><a
+    #end if
+    href="${tab}.html" title = "$title - $tab">$tab</a></li>
+  #end for
+    </ul>
+  </div>
+  <div id="content">
+    $content
+    A dollar: $$.
+  </div>
+</body>
diff --git a/examples/iupex1.nim b/examples/iupex1.nim
new file mode 100755
index 000000000..89e85e776
--- /dev/null
+++ b/examples/iupex1.nim
@@ -0,0 +1,37 @@
+# Example IUP program
+
+# IupTabs: Creates a IupTabs control.
+
+import iup
+
+discard iup.Open(nil, nil)
+
+var vbox1 = Iup.Vbox(Iup.Label("Inside Tab A"), Iup.Button("Button A", ""), nil)
+var vbox2 = Iup.Vbox(Iup.Label("Inside Tab B"), Iup.Button("Button B", ""), nil)
+
+Iup.SetAttribute(vbox1, "TABTITLE", "Tab A")
+Iup.SetAttribute(vbox2, "TABTITLE", "Tab B")
+
+var tabs1 = Iup.Tabs(vbox1, vbox2, nil)
+
+vbox1 = Iup.Vbox(Iup.Label("Inside Tab C"), Iup.Button("Button C", ""), nil)
+vbox2 = Iup.Vbox(Iup.Label("Inside Tab D"), Iup.Button("Button D", ""), nil)
+
+Iup.SetAttribute(vbox1, "TABTITLE", "Tab C")
+Iup.SetAttribute(vbox2, "TABTITLE", "Tab D")
+
+var tabs2 = Iup.Tabs(vbox1, vbox2, nil)
+Iup.SetAttribute(tabs2, "TABTYPE", "LEFT")
+
+var box = Iup.Hbox(tabs1, tabs2, nil)
+Iup.SetAttribute(box, "MARGIN", "10x10")
+Iup.SetAttribute(box, "GAP", "10")
+
+var dlg = Iup.Dialog(box)
+Iup.SetAttribute(dlg, "TITLE", "IupTabs")
+Iup.SetAttribute(dlg, "SIZE", "200x80")
+
+discard ShowXY(dlg, IUP_CENTER, IUP_CENTER)
+discard MainLoop()
+Close()
+
diff --git a/examples/keyval2.nim b/examples/keyval2.nim
new file mode 100755
index 000000000..5e7d0ea4a
--- /dev/null
+++ b/examples/keyval2.nim
@@ -0,0 +1,7 @@
+# Filter key=value pairs from "myfile.txt"
+import pegs
+
+for x in lines("myfile.txt"):
+  if x =~ peg"{\ident} \s* '=' \s* {.*}":
+    echo "Key: ", matches[1], 
+         " Value: ", matches[2]
diff --git a/examples/transff.nim b/examples/transff.nim
new file mode 100755
index 000000000..32d17e52c
--- /dev/null
+++ b/examples/transff.nim
@@ -0,0 +1,8 @@
+# Shows how to transform a file
+
+import pegs
+
+transformFile("infile.txt", "outfile.txt",
+  [(peg"""S <- {typedesc} \s* {\ident} \s* ','
+         typedesc <- \ident '*'* """, r"$2: $1")])
+
diff --git a/install.txt b/install.txt
index b8b2fefb5..e22a6effd 100755
--- a/install.txt
+++ b/install.txt
@@ -44,11 +44,10 @@ though.
 Installation on Windows
 -----------------------
 
-Install Nimrod by downloading and running
-the ``nimrod_$version.exe`` file. As default, the ``GCC`` 
-compiler is used that is bundled with this installer. **You can change
-the configuration file** ``config/nimrod.cfg`` **to use another C compiler 
-or change the path to GCC.**
+Install Nimrod by downloading and running the ``nimrod_$version.exe`` file.
+As default, the ``GCC`` compiler is used that is bundled with this installer.
+**You can change the configuration file** ``config/nimrod.cfg`` **to use
+another C compiler or change the path to GCC.**
 
 Currently, the following C compilers are supported under Windows:
 
diff --git a/koch.py b/koch.py
index 6430e4a6e..9a9f38649 100755
--- a/koch.py
+++ b/koch.py
@@ -12,7 +12,7 @@ from pycompab import *
 
 # --------------------- constants  ----------------------------------------
 
-NIMROD_VERSION = '0.8.1'
+NIMROD_VERSION = '0.8.2'
 # This string contains Nimrod's version. It is the only place
 # where the version needs to be updated. The rest is done by
 # the build process automatically. It is replaced **everywhere**!
@@ -327,8 +327,8 @@ def CogRule(name, filename, dependson):
 
 _nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim"))
 _output_obj = os.path.join(os.getcwd(), "obj")
-FPC_CMD = Subs(r"fpc -Cs16777216  -gl -bl -Crtoi -Sgidh -vw -Se1 -o$1 "
-               r"-FU$2 $3", _nim_exe, _output_obj,
+FPC_CMD = Subs("fpc -Cs16777216  -gl -bl -Crtoi -Sgidh -vw -Se1 -o\"$1\" "
+               "-FU\"$2\" \"$3\"", _nim_exe, _output_obj,
                os.path.join(os.getcwd(), "nim", "nimrod.pas"))
 
 def buildRod(options):
@@ -586,6 +586,7 @@ def cmd_clean(dir = "."):
   for f in Glob("*.idb"): Remove(f)
   for f in Glob("web/*.html"): Remove(f)
   for f in Glob("doc/*.html"): Remove(f)
+  for f in Glob("doc/*.pdf"): Remove(f)
   for f in Glob("rod/*.nim"): Remove(f) # remove generated source code
   def visit(extRegEx, dirname, names):
     if os.path.split(dirname)[1] == "nimcache":
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
new file mode 100755
index 000000000..197250d9c
--- /dev/null
+++ b/lib/impure/db_postgres.nim
@@ -0,0 +1,129 @@
+# Nimrod PostgreSQL database wrapper
+# (c) 2009 Andreas Rumpf
+
+import strutils, postgres
+
+type
+  TDbHandle* = PGconn
+  TRow* = seq[string]
+  EDb* = object of EIO
+  
+proc dbError(db: TDbHandle) {.noreturn.} = 
+  ## raises an EDb exception.
+  var e: ref EDb
+  new(e)
+  e.msg = PQerrorMessage(db)
+  raise e
+
+proc dbError*(msg: string) {.noreturn.} = 
+  ## raises an EDb exception with message `msg`.
+  var e: ref EDb
+  new(e)
+  e.msg = msg
+  raise e
+
+when false:
+  proc dbQueryOpt*(db: TDbHandle, query: string, args: openarray[string]) =
+    var stmt = mysql_stmt_init(db)
+    if stmt == nil: dbError(db)
+    if mysql_stmt_prepare(stmt, query, len(query)) != 0: 
+      dbError(db)
+    var 
+      bind: seq[MYSQL_BIND]
+    discard mysql_stmt_close(stmt)
+
+proc dbQuote(s: string): string =
+  result = "'"
+  for c in items(s):
+    if c == '\'': add(result, "''")
+    else: add(result, c)
+  add(result, '\'')
+
+proc dbFormat(formatstr: string, args: openarray[string]): string =
+  result = ""
+  var a = 0
+  for c in items(formatstr):
+    if c == '?':
+      add(result, dbQuote(args[a]))
+      inc(a)
+    else: 
+      add(result, c)
+  
+proc dbTryQuery*(db: TDbHandle, query: string, args: openarray[string]): bool =
+  var q = dbFormat(query, args)
+  var res = PQExec(db, q)
+  result = PQresultStatus(res) == PGRES_COMMAND_OK
+  PQclear(res)
+
+proc dbQuery*(db: TDbHandle, query: string, args: openarray[string]) =
+  var q = dbFormat(query, args)
+  var res = PQExec(db, q)
+  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  PQclear(res)
+  
+proc dbTryInsertID*(db: TDbHandle, query: string, 
+                    args: openarray[string]): int64 =
+  var q = dbFormat(query, args)
+  
+  
+  if mysqlRealQuery(db, q, q.len) != 0'i32: 
+    result = -1'i64
+  else:
+    result = mysql_insert_id(db)
+  LAST_INSERT_ID()
+
+proc dbInsertID*(db: TDbHandle, query: string, args: openArray[string]): int64 = 
+  result = dbTryInsertID(db, query, args)
+  if result < 0: dbError(db)
+  
+proc dbQueryAffectedRows*(db: TDbHandle, query: string, 
+                          args: openArray[string]): int64 = 
+  ## runs the query (typically "UPDATE") and returns the
+  ## number of affected rows
+  var q = dbFormat(query, args)
+  var res = PQExec(db, q)
+  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
+  result = parseBiggestInt($PQcmdTuples(res))
+  PQclear(res)
+  
+proc newRow(L: int): TRow =
+  newSeq(result, L)
+  for i in 0..L-1: result[i] = ""
+  
+iterator dbFastRows*(db: TDbHandle, query: string,
+                     args: openarray[string]): TRow =
+  var q = dbFormat(query, args)
+  var res = PQExec(db, q)
+  if PQresultStatus(res) != PGRES_TUPLES_OK: dbError(db)
+  var L = int(PQnfields(res))
+  var result = newRow(L)
+  for i in 0..PQntuples(res)-1:
+    for j in 0..L-1:
+      setLen(result[j], 0)
+      add(result[j], PQgetvalue(res, i, j))
+    yield result
+  PQclear(res)
+
+proc dbGetAllRows*(db: TDbHandle, query: string, 
+                   args: openarray[string]): seq[TRow] =
+  result = @[]
+  for r in dbFastRows(db, query, args):
+    result.add(r)
+
+iterator dbRows*(db: TDbHandle, query: string, 
+                 args: openarray[string]): TRow =
+  for r in items(dbGetAllRows(db, query, args)): yield r
+
+proc dbGetValue*(db: TDbHandle, query: string, 
+                 args: openarray[string]): string = 
+  result = ""
+  for row in dbFastRows(db, query, args): 
+    result = row[0]
+    break
+
+proc dbClose*(db: TDbHandle) = 
+  if db != nil: PQfinish(db)
+
+proc dbOpen*(connection, user, password, database: string): TDbHandle =
+  result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
+  if PQStatus(result) != CONNECTION_OK: result = nil
diff --git a/lib/nimbase.h b/lib/nimbase.h
index e62ad7096..a0f08f4f3 100755
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -92,8 +92,8 @@ __TINYC__
 #  define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
 #  define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
 
-#  define N_LIB_EXPORT  __declspec(dllexport)
-#  define N_LIB_IMPORT  __declspec(dllimport)
+#  define N_LIB_EXPORT  extern __declspec(dllexport)
+#  define N_LIB_IMPORT  extern __declspec(dllimport)
 #else
 #  define N_CDECL(rettype, name) rettype name
 #  define N_STDCALL(rettype, name) rettype name
@@ -107,7 +107,7 @@ __TINYC__
 #  define N_FASTCALL_PTR(rettype, name) rettype (*name)
 #  define N_SAFECALL_PTR(rettype, name) rettype (*name)
 
-#  define N_LIB_EXPORT
+#  define N_LIB_EXPORT  extern
 #  define N_LIB_IMPORT  extern
 #endif
 
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 5c8103b9a..ddeaec664 100755
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -1222,6 +1222,8 @@ var
   SC_XOPEN_STREAMS*{.importc: "_SC_XOPEN_STREAMS", header: "<unistd.h>".}: cint
   SC_XOPEN_UNIX*{.importc: "_SC_XOPEN_UNIX", header: "<unistd.h>".}: cint
   SC_XOPEN_VERSION*{.importc: "_SC_XOPEN_VERSION", header: "<unistd.h>".}: cint
+  SC_NPROCESSORS_ONLN*{.importc: "_SC_NPROCESSORS_ONLN", 
+                        header: "<unistd.h>".}: cint
   
   SEM_FAILED* {.importc, header: "<semaphore.h>".}: pointer
   IPC_CREAT* {.importc, header: "<sys/ipc.h>".}: cint
diff --git a/lib/pure/hashtabs.nim b/lib/pure/hashtabs.nim
new file mode 100755
index 000000000..68d19d63b
--- /dev/null
+++ b/lib/pure/hashtabs.nim
@@ -0,0 +1,163 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## The ``hashtabs`` module implements an efficient generic hash
+## table/dictionary data type. 
+
+import
+  hashes
+
+const
+  growthFactor = 2
+  startSize = 8
+  sham = sizeof(THash)*8-2 # shift amount
+  mask = 0b11 shl sham
+  usedSlot = 0b10 shl sham
+  delSlot =  0b01 shl sham
+  emptySlot = 0
+
+type
+  TTable*[TKey, TValue] = object
+    counter: int
+    data: seq[tuple[key: TKey, val: TValue, h: THash]]
+
+proc init*(t: var TTable, size = startSize) =
+  t.counter = 0
+  newSeq(t.data, size)
+
+proc markUsed(h: THash): THash {.inline.} =
+  return h and not mask or usedSlot
+
+proc len*(t: TTable): int {.inline.} =
+  ## returns the number of keys in `t`.
+  result = t.counter
+
+proc mustRehash(length, counter: int): bool =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+proc nextTry(h, maxHash: THash): THash {.inline.} =
+  result = ((5 * h) + 1) and maxHash
+
+template eq(a, b: expr): expr = a == b
+
+proc rawGet(t: TTable, key: TKey, fullhash: THash): int =
+  var h = fullhash and high(t.data)
+  while (t.data[h].h and mask) != 0:
+    # If it is a deleted entry, the comparison with ``markUsed(fullhash)``
+    # fails, so there is no need to check for this explicitely.
+    if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key): return h
+    h = nextTry(h, high(t.data))
+  result = - 1
+
+proc `[]`*(t: TTable, key: TKey): TValue =
+  ## retrieves the value at ``t[key]``. If `key` is not in `t`,
+  ## `EInvalidValue` is raised.
+  var index = rawGet(t, key, hash(key))
+  if index >= 0: result = t.data[index].val
+  else:
+    var e: ref EInvalidValue
+    new(e)
+    e.msg = "invalid key: " & $key
+    raise e
+
+proc hasKey*(t: TTable, key: TKey): bool =
+  ## returns true iff `key` is in the table `t`.
+  result = rawGet(t, key) >= 0
+
+proc rawInsert[TKey, TValue](
+               data: var seq[tuple[key: TKey, val: TValue, h: THash]],
+               tup: tuple[key: TKey, val: TValue, h: THash]) =
+  var h = tup.h and high(data)
+  while (data[h].h and mask) == usedSlot: h = nextTry(h, high(data))
+  data[h] = tup
+
+proc enlarge(t: var TTable) =
+  var n: seq[tuple[key: TKey, val: TValue, h: THash]]
+  newSeq(n, len(t.data) * growthFactor)
+  for i in 0..high(t.data):
+    if (t.data[i].h and mask) == usedSlot: rawInsert(n, t.data[i])
+  swap(t.data, n)
+
+proc `[]=`*(t: var TTable, key: TKey, val: TValue) =
+  ## puts a (key, value)-pair into `t`.
+  var fullhash = hash(key)
+  var index = rawGet(t, key, fullhash)
+  if index >= 0:
+    t.data[index].val = val
+  else:
+    if mustRehash(len(t.data), t.counter): enlarge(t)
+    rawInsert(t.data, (key, val, markUsed(fullhash)))
+    inc(t.counter)
+
+proc add*(t: var TTable, key: TKey, val: TValue) =
+  ## puts a (key, value)-pair into `t`, but does not check if key already
+  ## exists.
+  if mustRehash(len(t.data), t.counter): enlarge(t)
+  rawInsert(t.data, (key, val, markUsed(hash(key))))
+  inc(t.counter)
+
+proc del*(t: var TTable, key: TKey) =
+  ## deletes a (key, val)-pair in `t`.
+  var index = rawGet(t, key)
+  if index >= 0:
+    t.data[index].h = delSlot
+
+proc delAll*(t: var TTable, key: TKey) =
+  ## deletes all (key, val)-pairs in `t`.
+  while true:
+    var index = rawGet(t, key)
+    if index < 0: break
+    t.data[index].h = delSlot
+
+iterator pairs*(t: TTable): tuple[key: TKey, value: TValue] =
+  ## iterates over any (key, value) pair in the table `t`.
+  for h in 0..high(t.data):
+    if (t.data[h].h and mask) == usedSlot:
+      yield (t.data[h].key, t.data[h].val)
+
+iterator keys*(t: TTable): TKey =
+  ## iterate over any key in the table `t`. If key occurs multiple times, it
+  ## is yielded multiple times.
+  for h in 0..high(t.data):
+    if (t.data[h].h and mask) == usedSlot:
+      yield t.data[h].key
+
+iterator values*(t: TTable): TValue =
+  ## iterate over any value in the table `t`. 
+  for h in 0..high(t.data):
+    if (t.data[h].h and mask) == usedSlot:
+      yield t.data[h].val
+
+iterator values*(t: TTable, key: TKey): TValue =
+  ## iterate over any value associated with `key` in `t`.
+  var fullhash = hash(key)
+  var h = fullhash and high(t.data)
+  while (t.data[h].h and mask) != 0:
+    # If it is a deleted entry, the comparison with ``markUsed(fullhash)``
+    # fails, so there is no need to check for this explicitely.
+    if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key): 
+      yield t.data[h].val
+    h = nextTry(h, high(t.data))
+
+proc `$`*[KeyToStr=`$`, ValueToStr=`$`](t: TTable): string =
+  ## turns the table into its string representation. `$` must be available
+  ## for TKey and TValue for this to work.
+  if t.len == 0:
+    result = "{:}"
+  else:
+    result = "{"
+    var i = 0
+    for k, v in pairs(t):
+      if i > 0: add(result, ", ")
+      add(result, KeyToStr(k))
+      add(result, ": ")
+      add(result, ValueToStr(v))
+      inc(i)
+    add(result, "}")
diff --git a/lib/pure/macros.nim b/lib/pure/macros.nim
index 341a7cb61..5129cde1b 100755
--- a/lib/pure/macros.nim
+++ b/lib/pure/macros.nim
@@ -45,27 +45,26 @@ type
     nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, 
     nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, 
     nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted, 
-    nnkTableConstr, nnkQualified, nnkBind, nnkSymChoice, 
-    nnkHiddenStdConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, 
-    nnkCast, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, 
-    nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, 
-    nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, 
-    nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams, 
-    nnkOfInherit, nnkModule, nnkProcDef, nnkMethodDef, 
-    nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, 
-    nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, 
-    nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, 
-    nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt, 
-    nnkVarSection, nnkConstSection, nnkConstDef, nnkTypeSection, 
-    nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally, 
-    nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, 
-    nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, 
-    nnkFromStmt, nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, 
-    nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, 
-    nnkObjectTy, nnkTupleTy, nnkRecList, nnkRecCase, 
-    nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, 
-    nnkDistinctTy, nnkProcTy, nnkEnumTy, nnkEnumFieldDef, 
-    nnkReturnToken
+    nnkTableConstr, nnkBind, nnkSymChoice, nnkHiddenStdConv, 
+    nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, 
+    nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, 
+    nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, 
+    nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkAsgn, 
+    nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, 
+    nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef, 
+    nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 
+    nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 
+    nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, 
+    nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection, 
+    nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef, 
+    nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 
+    nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, 
+    nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, 
+    nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 
+    nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 
+    nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 
+    nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy, 
+    nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
   TNimNodeKinds* = set[TNimrodNodeKind]
   TNimrodTypeKind* = enum
     ntyNone, ntyBool, ntyChar, ntyEmpty, 
@@ -90,7 +89,7 @@ type
 #[[[end]]]

 

 type

-  TNimrodIdent = object of TObject

+  TNimrodIdent* = object of TObject

     ## represents a Nimrod identifier in the AST

 

   TNimrodSymbol {.final.} = object # hidden

@@ -134,7 +133,7 @@ proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
 

 proc add*(father: PNimrodNode, children: openArray[PNimrodNode]) {.

   magic: "NAddMultiple".}

-  ## adds each `children` to the `father` node

+  ## adds each child of `children` to the `father` node

 

 proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}

   ## deletes `n` children of `father` starting at index `idx`. 

diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 83cdbc0dd..a2a1830f7 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -790,7 +790,7 @@ iterator walkFiles*(pattern: string): string =
     if res != -1:
       while true:
         if f.cFileName[0] != '.':
-          yield extractDir(pattern) / extractFilename($f.cFileName)
+          yield splitFile(pattern).dir / extractFilename($f.cFileName)
         if findnextFileA(res, f) == 0'i32: break
       findclose(res)
   else: # here we use glob
@@ -811,8 +811,12 @@ type
   TPathComponent* = enum  ## Enumeration specifying a path component.
     pcFile,               ## path refers to a file
     pcLinkToFile,         ## path refers to a symbolic link to a file
-    pcDirectory,          ## path refers to a directory
-    pcLinkToDirectory     ## path refers to a symbolic link to a directory
+    pcDir,                ## path refers to a directory
+    pcLinkToDir           ## path refers to a symbolic link to a directory
+
+const
+  pcDirectory* {.deprecated.} = pcDir ## deprecated alias 
+  pcLinkToDirectory* {.deprecated.} = pcLinkToDir ## deprecated alias
 
 iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
   ## walks over the directory `dir` and yields for each directory or file in
@@ -843,7 +847,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
         var k = pcFile
         if f.cFilename[0] != '.':
           if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
-            k = pcDirectory
+            k = pcDir
           yield (k, dir / extractFilename($f.cFilename))
         if findnextFileA(h, f) == 0'i32: break
       findclose(h)
@@ -859,11 +863,33 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
           y = dir / y
           if stat(y, s) < 0'i32: break
           var k = pcFile
-          if S_ISDIR(s.st_mode): k = pcDirectory
+          if S_ISDIR(s.st_mode): k = pcDir
           if S_ISLNK(s.st_mode): k = succ(k)
           yield (k, y)
       discard closeDir(d)
 
+iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
+  ## walks over the directory `dir` and yields for each file in `dir`. The 
+  ## full path for each file is returned.
+  ## Walking is recursive. `filter` controls the behaviour of the iterator:
+  ##
+  ## ---------------------   ---------------------------------------------
+  ## filter                  meaning
+  ## ---------------------   ---------------------------------------------
+  ## ``pcFile``              yield real files
+  ## ``pcLinkToFile``        yield symbol links to files
+  ## ``pcDir``               follow real directories
+  ## ``pcLinkToDir``         follow symbol links to directories
+  ## ---------------------   ---------------------------------------------
+  ## 
+  var stack = @[dir]
+  while stack.len > 0:
+    for k,p in walkDir(stack.pop()):
+      if k in filter:
+        case k
+        of pcFile, pcLinkToFile: yield p
+        of pcDir, pcLinkToDir: stack.add(p)
+
 proc rawRemoveDir(dir: string) = 
   when defined(windows):
     if RemoveDirectoryA(dir) == 0'i32: OSError()
@@ -871,12 +897,12 @@ proc rawRemoveDir(dir: string) =
     if rmdir(dir) != 0'i32: OSError()
 
 proc removeDir*(dir: string) =
-  ## Removes the directory `dir` including all subdirectories or files
+  ## Removes the directory `dir` including all subdirectories and files
   ## in `dir` (recursively). If this fails, `EOS` is raised.
   for kind, path in walkDir(dir): 
     case kind
-    of pcFile, pcLinkToFile, pcLinkToDirectory: removeFile(path)
-    of pcDirectory: removeDir(dir)
+    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
+    of pcDir: removeDir(dir)
   rawRemoveDir(dir)
 
 proc rawCreateDir(dir: string) =
@@ -935,7 +961,7 @@ type
     fpOthersRead           ## read access for others
 
 proc getFilePermissions*(filename: string): set[TFilePermission] =
-  ## retrives file permissions for `filename`. `OSError` is raised in case of
+  ## retrieves file permissions for `filename`. `OSError` is raised in case of
   ## an error. On Windows, only the ``readonly`` flag is checked, every other
   ## permission is available in any case.
   when defined(posix):
@@ -1103,4 +1129,14 @@ proc getApplicationDir*(): string =
   ## Returns the directory of the application's executable.
   result = splitFile(getApplicationFilename()).dir
 
+proc sleep*(milsecs: int) =
+  ## sleeps `milsecs` milliseconds.
+  when defined(windows):
+    winlean.sleep(int32(milsecs))
+  else:
+    var a, b: Ttimespec
+    a.tv_sec = TTime(milsecs div 1000)
+    a.tv_nsec = (milsecs mod 1000) * 1000
+    discard posix.nanosleep(a, b)
+
 {.pop.}
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 7d017616e..d76825531 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -9,19 +9,19 @@
 
 ## This module implements an advanced facility for executing OS processes
 ## and process communication.
-## **On Windows this module does not work properly. Please help!**
 
 import
-  os, strtabs, streams
+  strutils, os, strtabs, streams
 
 when defined(windows):
   import winlean
+else:
+  import posix
 
 type
   TProcess = object of TObject
     when defined(windows):
       FProcessHandle: Thandle
-      FThreadHandle: Thandle
       inputHandle, outputHandle, errorHandle: TFileHandle
     else:
       inputHandle, outputHandle, errorHandle: TFileHandle
@@ -31,10 +31,11 @@ type
   PProcess* = ref TProcess ## represents an operating system process
 
   TProcessOption* = enum ## options that can be passed `startProcess`
-    poNone,              ## none option
+    poEchoCmd,           ## echo the command before execution
     poUseShell,          ## use the shell to execute the command; NOTE: This
                          ## often creates a security whole!
-    poStdErrToStdOut     ## merge stdout and stderr to the stdout stream
+    poStdErrToStdOut,    ## merge stdout and stderr to the stdout stream
+    poParentStreams      ## use the parent's streams
 
 proc execProcess*(command: string,
                   options: set[TProcessOption] = {poStdErrToStdOut,
@@ -77,18 +78,6 @@ proc startProcess*(command: string,
   ## Return value: The newly created process object. Nil is never returned,
   ## but ``EOS`` is raised in case of an error.
 
-when true:
-  nil
-else:
-  proc startGUIProcess*(command: string,
-                     workingDir: string = "",
-                     args: openarray[string] = [],
-                     env: PStringTable = nil,
-                     x = -1,
-                     y = -1,
-                     width = -1,
-                     height = -1): PProcess
-
 proc suspend*(p: PProcess)
   ## Suspends the process `p`.
 
@@ -117,13 +106,108 @@ proc outputStream*(p: PProcess): PStream
 proc errorStream*(p: PProcess): PStream
   ## returns ``p``'s output stream for reading from
 
+when defined(macosx) or defined(bsd):
+  const
+    CTL_HW = 6
+    HW_AVAILCPU = 25
+    HW_NCPU = 3
+  proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
+              a: var int, b: pointer, c: int): cint {.
+             importc: "sysctl", header: "<sys/sysctl.h>".}
+
+proc countProcessors*(): int = 
+  ## returns the numer of the processors/cores the machine has.
+  ## Returns 0 if it cannot be determined.
+  when defined(windows):
+    var x = getenv("NUMBER_OF_PROCESSORS")
+    if x.len > 0: result = parseInt(x)
+  elif defined(macosx) or defined(bsd):
+    var
+      mib: array[0..3, cint]
+      len, numCPU: int
+    mib[0] = CTL_HW
+    mib[1] = HW_AVAILCPU
+    len = sizeof(numCPU)
+    discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
+    if numCPU < 1:
+      mib[1] = HW_NCPU
+      discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
+    result = numCPU
+  elif defined(hpux):
+    result = mpctl(MPC_GETNUMSPUS, nil, nil)
+  elif defined(irix):
+    var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
+    result = sysconf(SC_NPROC_ONLN)
+  else:
+    result = sysconf(SC_NPROCESSORS_ONLN)
+  if result <= 0: result = 1
+
+proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
+  var c = parseCmdLine(cmd)
+  var a: seq[string] = @[] # slicing is not yet implemented :-(
+  for i in 1 .. c.len-1: add(a, c[i])
+  result = startProcess(command=c[0], args=a, options=options)
+
+proc execProcesses*(cmds: openArray[string],
+                    options = {poStdErrToStdOut, poParentStreams},
+                    n = countProcessors()): int =
+  ## executes the commands `cmds` in parallel. Creates `n` processes
+  ## that execute in parallel. The highest return value of all processes
+  ## is returned.
+  assert n > 0
+  if n > 1:
+    var q: seq[PProcess]
+    newSeq(q, n)
+    var m = min(n, cmds.len)
+    for i in 0..m-1:
+      q[i] = startProcessAux(cmds[i], options=options)
+    when defined(noBusyWaiting):
+      var r = 0
+      for i in m..high(cmds):
+        when defined(debugExecProcesses):
+          var err = ""
+          var outp = outputStream(q[r])
+          while running(q[r]) or not outp.atEnd(outp):
+            err.add(outp.readLine())
+            err.add("\n")
+          echo(err)
+        result = max(waitForExit(q[r]), result)
+        q[r] = startProcessAux(cmds[i], options=options)
+        r = (r + 1) mod n
+    else:
+      var i = m
+      while i <= high(cmds):
+        sleep(50)
+        for r in 0..n-1:
+          if not running(q[r]):
+            #echo(outputStream(q[r]).readLine())
+            result = max(waitForExit(q[r]), result)
+            q[r] = startProcessAux(cmds[i], options=options)
+            inc(i)
+            if i > high(cmds): break
+    for i in 0..m-1:
+      result = max(waitForExit(q[i]), result)  
+  else:
+    for i in 0..high(cmds):
+      var p = startProcessAux(cmds[i], options=options)
+      result = max(waitForExit(p), result)
+
+when true:
+  nil
+else:
+  proc startGUIProcess*(command: string,
+                     workingDir: string = "",
+                     args: openarray[string] = [],
+                     env: PStringTable = nil,
+                     x = -1,
+                     y = -1,
+                     width = -1,
+                     height = -1): PProcess
+
 proc execProcess(command: string,
                  options: set[TProcessOption] = {poStdErrToStdOut,
                                                  poUseShell}): string =
-  var c = parseCmdLine(command)
-  var a: seq[string] = @[] # slicing is not yet implemented :-(
-  for i in 1 .. c.len-1: add(a, c[i])
-  var p = startProcess(command=c[0], args=a, options=options)
+  var p = startProcessAux(command, options=options)
   var outp = outputStream(p)
   result = ""
   while running(p) or not outp.atEnd(outp):
@@ -147,15 +231,19 @@ when defined(Windows):
       atTheEnd: bool
 
   proc hsClose(s: PFileHandleStream) = nil # nothing to do here
-  proc hsAtEnd(s: PFileHandleStream): bool = return true
+  proc hsAtEnd(s: PFileHandleStream): bool = return s.atTheEnd
 
   proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
+    if s.atTheEnd: return 0
     var br: int32
     var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil)
-    if a == 0: OSError()
+    # TRUE and zero bytes returned (EOF).
+    # TRUE and n (>0) bytes returned (good data).
+    # FALSE and bytes returned undefined (system error).
+    if a == 0 and br != 0: OSError()
+    s.atTheEnd = br < bufLen
     result = br
-    #atEnd = bytesRead < bufLen
-
+  
   proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
     var bytesWritten: int32
     var a = winlean.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
@@ -168,18 +256,14 @@ when defined(Windows):
     result.atEnd = hsAtEnd
     result.readData = hsReadData
     result.writeData = hsWriteData
-
+    
   proc buildCommandLine(a: string, args: openarray[string]): cstring =
-    var L = a.len
-    for i in 0..high(args): inc(L, args[i].len+1)
-    result = cast[cstring](alloc0(L+1))
-    copyMem(result, cstring(a), a.len)
-    L = a.len
-    for i in 0..high(args):
-      result[L] = ' '
-      inc(L)
-      copyMem(addr(result[L]), cstring(args[i]), args[i].len)
-      inc(L, args[i].len)
+    var res = quoteIfContainsWhite(a)
+    for i in 0..high(args): 
+      res.add(' ')
+      res.add(quoteIfContainsWhite(args[i]))
+    result = cast[cstring](alloc0(res.len+1))
+    copyMem(result, cstring(res), res.len)
 
   proc buildEnv(env: PStringTable): cstring =
     var L = 0
@@ -198,72 +282,81 @@ when defined(Windows):
   #  O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
   #  O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
 
-  proc CreatePipeHandles(Inhandle, OutHandle: var THandle) =
+  proc CreatePipeHandles(Rdhandle, WrHandle: var THandle) =
     var piInheritablePipe: TSecurityAttributes
     piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
     piInheritablePipe.lpSecurityDescriptor = nil
     piInheritablePipe.Binherithandle = 1
-    if CreatePipe(Inhandle, Outhandle, piInheritablePipe, 0) == 0'i32:
+    if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
       OSError()
 
-  proc startProcess*(command: string,
+  proc fileClose(h: THandle) {.inline.} =
+    if h > 4: discard CloseHandle(h)
+
+  proc startProcess(command: string,
                  workingDir: string = "",
                  args: openarray[string] = [],
                  env: PStringTable = nil,
                  options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
-    new(result)
     var
       SI: TStartupInfo
       ProcInfo: TProcessInformation
       success: int
       hi, ho, he: THandle
+    new(result)
     SI.cb = SizeOf(SI)
-    SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
-    CreatePipeHandles(SI.hStdInput, HI)
-    CreatePipeHandles(HO, Si.hStdOutput)
-    if poStdErrToStdOut in options:
-      SI.hStdError = SI.hStdOutput
-      HE = HO
+    if poParentStreams notin options:
+      SI.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
+      CreatePipeHandles(SI.hStdInput, HI)
+      CreatePipeHandles(HO, Si.hStdOutput)
+      if poStdErrToStdOut in options:
+        SI.hStdError = SI.hStdOutput
+        HE = HO
+      else:
+        CreatePipeHandles(HE, Si.hStdError)
+      result.inputHandle = hi
+      result.outputHandle = ho
+      result.errorHandle = he
     else:
-      CreatePipeHandles(HE, Si.hStdError)
-    result.inputHandle = hi
-    result.outputHandle = ho
-    result.errorHandle = he
+      SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
+      SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
+      SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
+      result.inputHandle = si.hStdInput
+      result.outputHandle = si.hStdOutput
+      result.errorHandle = si.hStdError
+    
     var cmdl: cstring
-    if poUseShell in options:
-      var comspec = getEnv("COMSPEC")
-      var a: seq[string] = @[]
-      add(a, "/c")
-      add(a, command)
-      add(a, args)
-      cmdl = buildCommandLine(comspec, a)
+    if false: # poUseShell in options:
+      cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
     else:
       cmdl = buildCommandLine(command, args)
     var wd: cstring = nil
+    var e: cstring = nil
     if len(workingDir) > 0: wd = workingDir
-    if env == nil:
-      success = winlean.CreateProcess(nil,
-        cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo)
-    else:
-      var e = buildEnv(env)
-      success = winlean.CreateProcess(nil,
-        cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
-      dealloc(e)
+    if env != nil: e = buildEnv(env)
+    if poEchoCmd in options: echo($cmdl)
+    success = winlean.CreateProcess(nil,
+      cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
+    
+    if poParentStreams notin options:
+      FileClose(si.hStdInput)
+      FileClose(si.hStdOutput)
+      if poStdErrToStdOut notin options:
+        FileClose(si.hStdError)
+      
+    if e != nil: dealloc(e)
     dealloc(cmdl)
-    if success == 0:
-      OSError()
-    # NEW:
-    # Close the handles now so anyone waiting is woken.
+    if success == 0: OSError()
+    # Close the handle now so anyone waiting is woken:
     discard closeHandle(procInfo.hThread)
     result.FProcessHandle = procInfo.hProcess
-    result.FThreadHandle = procInfo.hThread
     result.id = procInfo.dwProcessID
 
   proc suspend(p: PProcess) =
-    discard SuspendThread(p.FThreadHandle)
+    discard SuspendThread(p.FProcessHandle)
 
   proc resume(p: PProcess) =
-    discard ResumeThread(p.FThreadHandle)
+    discard ResumeThread(p.FProcessHandle)
 
   proc running(p: PProcess): bool =
     var x = waitForSingleObject(p.FProcessHandle, 50)
@@ -274,7 +367,6 @@ when defined(Windows):
       discard TerminateProcess(p.FProcessHandle, 0)
 
   proc waitForExit(p: PProcess): int =
-    #CloseHandle(p.FThreadHandle)
     discard WaitForSingleObject(p.FProcessHandle, Infinite)
     var res: int32
     discard GetExitCodeProcess(p.FProcessHandle, res)
@@ -314,17 +406,15 @@ when defined(Windows):
       discard CloseHandle(Process)
 
 else:
-  import posix
-
   const
     readIdx = 0
     writeIdx = 1
 
   proc addCmdArgs(command: string, args: openarray[string]): string =
-    result = command
+    result = quoteIfContainsWhite(command)
     for i in 0 .. high(args):
       add(result, " ")
-      add(result, args[i])
+      add(result, quoteIfContainsWhite(args[i]))
 
   proc toCStringArray(b, a: openarray[string]): cstringArray =
     result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
@@ -344,14 +434,15 @@ else:
       copyMem(result[i], addr(x[0]), x.len+1)
       inc(i)
 
-  proc startProcess*(command: string,
+  proc startProcess(command: string,
                  workingDir: string = "",
                  args: openarray[string] = [],
                  env: PStringTable = nil,
                  options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
-    new(result)
     var
       p_stdin, p_stdout, p_stderr: array [0..1, cint]
+    new(result)
+    result.exitCode = 3 # for ``waitForExit``
     if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32:
       OSError("failed to create a pipe")
     var Pid = fork()
@@ -389,6 +480,8 @@ else:
       # too risky to raise an exception here:
       quit("execve call failed: " & $strerror(errno))
     # Parent process. Copy process information.
+    if poEchoCmd in options:
+      echo(command & " " & join(args, " "))
     result.id = pid
 
     result.inputHandle = p_stdin[writeIdx]
@@ -415,9 +508,15 @@ else:
       if running(p): discard kill(p.id, SIGKILL)
 
   proc waitForExit(p: PProcess): int =
-    result = 1
-    if waitPid(p.id, p.exitCode, 0) == int(p.id):
-      result = p.exitCode
+    #if waitPid(p.id, p.exitCode, 0) == int(p.id):
+    # ``waitPid`` fails if the process is not running anymore. But then
+    # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
+    # initialized with 3, wrong success exit codes are prevented.
+    var oldExitCode = p.exitCode
+    if waitPid(p.id, p.exitCode, 0) < 0:
+      # failed, so restore old exitCode
+      p.exitCode = oldExitCode
+    result = int(p.exitCode)
 
   proc inputStream(p: PProcess): PStream =
     var f: TFile
@@ -440,4 +539,5 @@ else:
     result = csystem(command)
 
 when isMainModule:
-  echo execCmd("gcc -v")
+  var x = execProcess("gcc -v")
+  echo "ECHO ", x
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
new file mode 100755
index 000000000..c029f92a2
--- /dev/null
+++ b/lib/pure/pegs.nim
@@ -0,0 +1,1320 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Simple PEG (Parsing expression grammar) matching. Uses no memorization, but
+## uses superoperators and symbol inlining to improve performance. Note:
+## Matching performance is hopefully competitive with optimized regular
+## expression engines.
+##
+## .. include:: ../doc/pegdocs.txt
+##
+
+const
+  useUnicode = true ## change this to deactivate proper UTF-8 support
+
+import
+  strutils
+
+when useUnicode:
+  import unicode
+
+const
+  InlineThreshold = 5   ## number of leaves; -1 to disable inlining
+
+type
+  TPegKind = enum
+    pkEmpty,
+    pkAny,              ## any character (.)
+    pkAnyRune,          ## any Unicode character (_)
+    pkNewLine,          ## CR-LF, LF, CR
+    pkTerminal,
+    pkTerminalIgnoreCase,
+    pkTerminalIgnoreStyle,
+    pkChar,             ## single character to match
+    pkCharChoice,
+    pkNonTerminal,
+    pkSequence,         ## a b c ... --> Internal DSL: peg(a, b, c)
+    pkOrderedChoice,    ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
+    pkGreedyRep,        ## a*     --> Internal DSL: *a
+                        ## a+     --> Internal DSL: +a; represented as (a a*)
+    pkGreedyRepChar,    ## x* where x is a single character (superop)
+    pkGreedyRepSet,     ## [set]* (superop)
+    pkGreedyAny,        ## .* or _* (superop)
+    pkOption,           ## a?     --> Internal DSL: ?a
+    pkAndPredicate,     ## &a     --> Internal DSL: &a
+    pkNotPredicate,     ## !a     --> Internal DSL: !a
+    pkCapture,          ## {a}    --> Internal DSL: capture(a)
+    pkRule,             ## a <- b
+    pkList              ## a, b
+  TNonTerminalFlag = enum
+    ntDeclared, ntUsed
+  TNonTerminal {.final.} = object ## represents a non terminal symbol
+    name: string        ## the name of the symbol
+    line: int           ## the line the symbol has been declared/used in
+    col: int            ## the column the symbol has been declared/used in
+    flags: set[TNonTerminalFlag] ## the nonterminal's flags
+    rule: TNode         ## the rule that the symbol refers to
+  TNode {.final.} = object
+    case kind: TPegKind
+    of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine: nil
+    of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
+    of pkChar, pkGreedyRepChar: ch: char
+    of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
+    of pkNonTerminal: nt: PNonTerminal
+    else: sons: seq[TNode]
+  PNonTerminal* = ref TNonTerminal
+  
+  TPeg* = TNode ## type that represents a PEG
+
+proc term*(t: string): TPeg =
+  ## constructs a PEG from a terminal string
+  if t.len != 1:  
+    result.kind = pkTerminal
+    result.term = t
+  else:
+    result.kind = pkChar
+    result.ch = t[0]
+
+proc termIgnoreCase*(t: string): TPeg =
+  ## constructs a PEG from a terminal string; ignore case for matching
+  result.kind = pkTerminalIgnoreCase
+  result.term = t
+
+proc termIgnoreStyle*(t: string): TPeg =
+  ## constructs a PEG from a terminal string; ignore style for matching
+  result.kind = pkTerminalIgnoreStyle
+  result.term = t
+
+proc term*(t: char): TPeg =
+  ## constructs a PEG from a terminal char
+  assert t != '\0'
+  result.kind = pkChar
+  result.ch = t
+  
+proc charSet*(s: set[char]): TPeg =
+  ## constructs a PEG from a character set `s`
+  assert '\0' notin s
+  result.kind = pkCharChoice
+  new(result.charChoice)
+  result.charChoice^ = s
+
+proc len(a: TPeg): int {.inline.} = return a.sons.len
+proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
+
+proc addChoice(dest: var TPeg, elem: TPeg) =
+  var L = dest.len-1
+  if L >= 0 and dest.sons[L].kind == pkCharChoice: 
+    case elem.kind
+    of pkCharChoice:
+      dest.sons[L].charChoice^ = dest.sons[L].charChoice^ + elem.charChoice^
+    of pkChar: incl(dest.sons[L].charChoice^, elem.ch)
+    else: add(dest, elem)
+  else: add(dest, elem)
+
+template multipleOp(k: TPegKind, localOpt: expr) =
+  result.kind = k
+  result.sons = @[]
+  for x in items(a):
+    if x.kind == k:
+      for y in items(x.sons):
+        localOpt(result, y)
+    else:
+      localOpt(result, x)
+  if result.len == 1:
+    result = result.sons[0]
+
+proc `/`*(a: openArray[TPeg]): TPeg =
+  ## constructs an ordered choice with the PEGs in `a`
+  multipleOp(pkOrderedChoice, addChoice)
+
+proc addSequence(dest: var TPeg, elem: TPeg) =
+  var L = dest.len-1
+  if L >= 0 and dest.sons[L].kind == pkTerminal: 
+    case elem.kind
+    of pkTerminal: add(dest.sons[L].term, elem.term)
+    of pkChar: add(dest.sons[L].term, elem.ch)
+    else: add(dest, elem)
+  else: add(dest, elem)
+
+proc sequence*(a: openArray[TPeg]): TPeg =
+  ## constructs a sequence with all the PEGs from `a`
+  multipleOp(pkSequence, addSequence)
+ 
+proc `?`*(a: TPeg): TPeg =
+  ## constructs an optional piece with the PEG `a`
+  if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
+                pkGreedyRepSet}:
+    # a* ?  --> a*
+    # a? ?  --> a?
+    result = a
+  else:
+    result.kind = pkOption
+    result.sons = @[a]
+
+proc `*`*(a: TPeg): TPeg =
+  ## constructs a "greedy repetition" piece the PEG `a`
+  case a.kind
+  of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
+    assert false
+    # produces endless loop!
+  of pkChar:
+    result.kind = pkGreedyRepChar
+    result.ch = a.ch
+  of pkCharChoice:
+    result.kind = pkGreedyRepSet
+    result.charChoice = a.charChoice # copying a reference suffices!
+  of pkAny, pkAnyRune:
+    result.kind = pkGreedyAny
+  else:
+    result.kind = pkGreedyRep
+    result.sons = @[a]
+  
+proc `+`*(a: TPeg): TPeg =
+  ## constructs a "greedy positive repetition" with the PEG `a`
+  return sequence(a, *a)
+  
+proc `&`*(a: TPeg): TPeg =
+  ## constructs an "and predicate" with the PEG `a`
+  result.kind = pkAndPredicate
+  result.sons = @[a]
+
+proc `!`*(a: TPeg): TPeg =
+  ## constructs a "not predicate" with the PEG `a`
+  result.kind = pkNotPredicate
+  result.sons = @[a]
+
+proc any*: TPeg {.inline.} =
+  ## constructs the PEG `any character`:idx: (``.``)
+  result.kind = pkAny
+
+proc anyRune*: TPeg {.inline.} =
+  ## constructs the PEG `any rune`:idx: (``_``)
+  result.kind = pkAnyRune
+
+proc newLine*: TPeg {.inline.} =
+  ## constructs the PEG `newline`:idx: (``\n``)
+  result.kind = pkNewline
+
+proc capture*(a: TPeg): TPeg =
+  ## constructs a capture with the PEG `a`
+  result.kind = pkCapture
+  result.sons = @[a]
+
+proc spaceCost(n: TPeg): int =
+  case n.kind
+  of pkEmpty: nil
+  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
+     pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny, pkAnyRune,
+     pkNewLine, pkGreedyAny:
+    result = 1
+  of pkNonTerminal:
+    # we cannot inline a rule with a non-terminal
+    result = InlineThreshold+1
+  else:
+    for i in 0..n.len-1:
+      inc(result, spaceCost(n.sons[i]))
+      if result >= InlineThreshold: break
+
+proc nonterminal*(n: PNonTerminal): TPeg = 
+  ## constructs a PEG that consists of the nonterminal symbol
+  assert n != nil
+  if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
+    when false: echo "inlining symbol: ", n.name
+    result = n.rule # inlining of rule enables better optimizations
+  else:
+    result.kind = pkNonTerminal
+    result.nt = n
+
+proc newNonTerminal*(name: string, line, column: int): PNonTerminal =
+  ## constructs a nonterminal symbol
+  new(result)
+  result.name = name
+  result.line = line
+  result.col = column
+
+template letters*: expr =
+  ## expands to ``charset({'A'..'Z', 'a'..'z'})``
+  charset({'A'..'Z', 'a'..'z'})
+  
+template digits*: expr =
+  ## expands to ``charset({'0'..'9'})``
+  charset({'0'..'9'})
+
+template whitespace*: expr =
+  ## expands to ``charset({' ', '\9'..'\13'})``
+  charset({' ', '\9'..'\13'})
+  
+template identChars*: expr =
+  ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
+  charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})
+  
+template identStartChars*: expr =
+  ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
+  charset({'a'..'z', 'A'..'Z', '_'})
+
+template ident*: expr =
+  ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
+  sequence(charset({'a'..'z', 'A'..'Z', '_'}),
+           *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
+  
+template natural*: expr =
+  ## same as ``\d+``
+  +digits
+
+const
+  MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that
+                       ## can be captured. More subpatterns cannot be captured! 
+
+# --------------------- core engine -------------------------------------------
+
+type
+  TMatchClosure {.final.} = object
+    matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
+    ml: int
+
+when not useUnicode:
+  type
+    TRune = char
+  template fastRuneAt(s, i, ch: expr) =
+    ch = s[i]
+    inc(i)
+  template runeLenAt(s, i: expr): expr = 1
+
+proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
+  ## this implements a simple PEG interpreter. Thanks to superoperators it
+  ## has competitive performance nevertheless.
+  ## Returns -1 if it does not match, else the length of the match
+  case p.kind
+  of pkEmpty: result = 0 # match of length 0
+  of pkAny:
+    if s[start] != '\0': result = 1
+    else: result = -1
+  of pkAnyRune:
+    if s[start] != '\0':
+      result = runeLenAt(s, start)
+    else:
+      result = -1
+  of pkGreedyAny:
+    result = len(s) - start
+  of pkNewLine:
+    if s[start] == '\L': result = 1
+    elif s[start] == '\C':
+      if s[start+1] == '\L': result = 2
+      else: result = 1
+    else: result = -1
+  of pkTerminal:
+    result = len(p.term)
+    for i in 0..result-1:
+      if p.term[i] != s[start+i]:
+        result = -1
+        break
+  of pkTerminalIgnoreCase:
+    var
+      i = 0
+      a, b: TRune
+    result = start
+    while i < len(p.term):
+      fastRuneAt(p.term, i, a)
+      fastRuneAt(s, result, b)
+      if toLower(a) != toLower(b):
+        result = -1
+        break
+    dec(result, start)
+  of pkTerminalIgnoreStyle:
+    var
+      i = 0
+      a, b: TRune
+    result = start
+    while i < len(p.term):
+      while true:
+        fastRuneAt(p.term, i, a)
+        if a != TRune('_'): break
+      while true:
+        fastRuneAt(s, result, b)
+        if b != TRune('_'): break
+      if toLower(a) != toLower(b):
+        result = -1
+        break
+    dec(result, start)
+  of pkChar:
+    if p.ch == s[start]: result = 1
+    else: result = -1
+  of pkCharChoice:
+    if contains(p.charChoice^, s[start]): result = 1
+    else: result = -1
+  of pkNonTerminal:
+    var oldMl = c.ml
+    when false: echo "enter: ", p.nt.name
+    result = m(s, p.nt.rule, start, c)
+    when false: echo "leave: ", p.nt.name
+    if result < 0: c.ml = oldMl
+  of pkSequence:
+    var oldMl = c.ml  
+    result = 0
+    for i in 0..high(p.sons):
+      var x = m(s, p.sons[i], start+result, c)
+      if x < 0:
+        c.ml = oldMl
+        result = -1
+        break
+      else: inc(result, x)
+  of pkOrderedChoice:
+    var oldMl = c.ml
+    for i in 0..high(p.sons):
+      result = m(s, p.sons[i], start, c)
+      if result >= 0: break
+      c.ml = oldMl
+  of pkGreedyRep:
+    result = 0
+    while true:
+      var x = m(s, p.sons[0], start+result, c)
+      # if x == 0, we have an endless loop; so the correct behaviour would be
+      # not to break. But endless loops can be easily introduced:
+      # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
+      # expected thing in this case.
+      if x <= 0: break
+      inc(result, x)
+  of pkGreedyRepChar:
+    result = 0
+    var ch = p.ch
+    while ch == s[start+result]: inc(result)
+  of pkGreedyRepSet:
+    result = 0
+    while contains(p.charChoice^, s[start+result]): inc(result)
+  of pkOption:
+    result = max(0, m(s, p.sons[0], start, c))
+  of pkAndPredicate:
+    var oldMl = c.ml
+    result = m(s, p.sons[0], start, c)
+    if result >= 0: result = 0 # do not consume anything
+    else: c.ml = oldMl
+  of pkNotPredicate:
+    var oldMl = c.ml
+    result = m(s, p.sons[0], start, c)
+    if result < 0: result = 0
+    else:
+      c.ml = oldMl
+      result = -1
+  of pkCapture:
+    var idx = c.ml # reserve a slot for the subpattern
+    inc(c.ml)
+    result = m(s, p.sons[0], start, c)
+    if result >= 0:
+      if idx < maxSubpatterns:
+        c.matches[idx] = (start, start+result-1)
+      #else: silently ignore the capture
+    else:
+      c.ml = idx
+  of pkRule, pkList: assert false
+
+proc match*(s: string, pattern: TPeg, matches: var openarray[string],
+            start = 0): bool =
+  ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
+  ## the captured substrings in the array ``matches``. If it does not
+  ## match, nothing is written into ``matches`` and ``false`` is
+  ## returned.
+  var c: TMatchClosure
+  result = m(s, pattern, start, c) == len(s)
+  if result:
+    for i in 0..c.ml-1:
+      matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
+
+proc match*(s: string, pattern: TPeg, start = 0): bool =
+  ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
+  var c: TMatchClosure
+  result = m(s, pattern, start, c) == len(s)
+
+proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
+               start = 0): int =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, -1 is returned. Note that a match length
+  ## of zero can happen. It's possible that a suffix of `s` remains
+  ## that does not belong to the match.
+  var c: TMatchClosure
+  result = m(s, pattern, start, c)
+  if result >= 0:
+    for i in 0..c.ml-1:
+      matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
+
+proc matchLen*(s: string, pattern: TPeg, start = 0): int =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, -1 is returned. Note that a match length
+  ## of zero can happen. It's possible that a suffix of `s` remains
+  ## that does not belong to the match.
+  var c: TMatchClosure
+  result = m(s, pattern, start, c)
+
+proc find*(s: string, pattern: TPeg, matches: var openarray[string],
+           start = 0): int =
+  ## returns the starting position of ``pattern`` in ``s`` and the captured
+  ## substrings in the array ``matches``. If it does not match, nothing
+  ## is written into ``matches`` and -1 is returned.
+  for i in 0 .. s.len-1:
+    if matchLen(s, pattern, matches, i) >= 0: return i
+  return -1
+  # could also use the pattern here: (!P .)* P
+  
+proc find*(s: string, pattern: TPeg, start = 0): int =
+  ## returns the starting position of ``pattern`` in ``s``. If it does not
+  ## match, -1 is returned.
+  for i in 0 .. s.len-1:
+    if matchLen(s, pattern, i) >= 0: return i
+  return -1
+  
+template `=~`*(s: string, pattern: TPeg): expr =
+  ## This calls ``match`` with an implicit declared ``matches`` array that 
+  ## can be used in the scope of the ``=~`` call: 
+  ## 
+  ## .. code-block:: nimrod
+  ##
+  ##   if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": 
+  ##     # matches a key=value pair:
+  ##     echo("Key: ", matches[0])
+  ##     echo("Value: ", matches[1])
+  ##   elif line =~ peg"\s*{'#'.*}":
+  ##     # matches a comment
+  ##     # note that the implicit ``matches`` array is different from the
+  ##     # ``matches`` array of the first branch
+  ##     echo("comment: ", matches[0])
+  ##   else:
+  ##     echo("syntax error")
+  ##  
+  when not definedInScope(matches):
+    var matches: array[0..maxSubpatterns-1, string]
+  match(s, pattern, matches)
+
+# ------------------------- more string handling ------------------------------
+
+proc contains*(s: string, pattern: TPeg, start = 0): bool =
+  ## same as ``find(s, pattern, start) >= 0``
+  return find(s, pattern, start) >= 0
+
+proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
+              start = 0): bool =
+  ## same as ``find(s, pattern, matches, start) >= 0``
+  return find(s, pattern, matches, start) >= 0
+
+proc startsWith*(s: string, prefix: TPeg): bool =
+  ## returns true if `s` starts with the pattern `prefix`
+  result = matchLen(s, prefix) >= 0
+
+proc endsWith*(s: string, suffix: TPeg): bool =
+  ## returns true if `s` ends with the pattern `prefix`
+  for i in 0 .. s.len-1:
+    if matchLen(s, suffix, i) == s.len - i: return true
+
+proc replace*(s: string, sub: TPeg, by: string): string =
+  ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
+  ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
+  ##
+  ## .. code-block:: nimrod
+  ##   "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##   "var1<-keykey; val2<-key2key2"
+  result = ""
+  var i = 0
+  var caps: array[0..maxSubpatterns-1, string]
+  while i < s.len:
+    var x = matchLen(s, sub, caps, i)
+    if x <= 0:
+      add(result, s[i])
+      inc(i)
+    else:
+      addf(result, by, caps)
+      inc(i, x)
+  # copy the rest:
+  add(result, copy(s, i))
+  
+proc parallelReplace*(s: string, subs: openArray[
+                      tuple[pattern: TPeg, repl: string]]): string = 
+  ## Returns a modified copy of `s` with the substitutions in `subs`
+  ## applied in parallel.
+  result = ""
+  var i = 0
+  var caps: array[0..maxSubpatterns-1, string]
+  while i < s.len:
+    block searchSubs:
+      for j in 0..high(subs):
+        var x = matchLen(s, subs[j][0], caps, i)
+        if x > 0:
+          addf(result, subs[j][1], caps)
+          inc(i, x)
+          break searchSubs
+      add(result, s[i])
+      inc(i)
+  # copy the rest:
+  add(result, copy(s, i))  
+  
+proc transformFile*(infile, outfile: string,
+                    subs: openArray[tuple[pattern: TPeg, repl: string]]) =
+  ## reads in the file `infile`, performs a parallel replacement (calls
+  ## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
+  ## error occurs. This is supposed to be used for quick scripting.
+  var x = readFile(infile)
+  if not isNil(x):
+    var f: TFile
+    if open(f, outfile, fmWrite):
+      write(f, x.parallelReplace(subs))
+      close(f)
+    else:
+      quit("cannot open for writing: " & outfile)
+  else:
+    quit("cannot open for reading: " & infile)
+  
+iterator split*(s: string, sep: TPeg): string =
+  ## Splits the string `s` into substrings.
+  ##
+  ## Substrings are separated by the PEG `sep`.
+  ## Examples:
+  ##
+  ## .. code-block:: nimrod
+  ##   for word in split("00232this02939is39an22example111", peg"\d+"):
+  ##     writeln(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nimrod
+  ##   "this"
+  ##   "is"
+  ##   "an"
+  ##   "example"
+  ##
+  var
+    first = 0
+    last = 0
+  while last < len(s):
+    var x = matchLen(s, sep, last)
+    if x > 0: inc(last, x)
+    first = last
+    while last < len(s):
+      inc(last)
+      x = matchLen(s, sep, last)
+      if x > 0: break
+    if first < last:
+      yield copy(s, first, last-1)
+
+proc split*(s: string, sep: TPeg): seq[string] {.noSideEffect.} =
+  ## Splits the string `s` into substrings.
+  accumulateResult(split(s, sep))
+
+# ------------------------- debugging -----------------------------------------
+
+proc esc(c: char, reserved = {'\0'..'\255'}): string = 
+  case c
+  of '\b': result = "\\b"
+  of '\t': result = "\\t"
+  of '\c': result = "\\c"
+  of '\L': result = "\\l"
+  of '\v': result = "\\v"
+  of '\f': result = "\\f"
+  of '\e': result = "\\e"
+  of '\a': result = "\\a"
+  of '\\': result = "\\\\"
+  of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
+  elif c < ' ' or c >= '\128': result = '\\' & $ord(c)
+  elif c in reserved: result = '\\' & c
+  else: result = $c
+  
+proc singleQuoteEsc(c: Char): string = return "'" & esc(c, {'\''}) & "'"
+
+proc singleQuoteEsc(str: string): string = 
+  result = "'"
+  for c in items(str): add result, esc(c, {'\''})
+  add result, '\''
+  
+proc charSetEscAux(cc: set[char]): string = 
+  const reserved = {'^', '-', ']'}
+  result = ""
+  var c1 = 0
+  while c1 <= 0xff: 
+    if chr(c1) in cc: 
+      var c2 = c1
+      while c2 < 0xff and chr(succ(c2)) in cc: inc(c2)
+      if c1 == c2: 
+        add result, esc(chr(c1), reserved)
+      elif c2 == succ(c1): 
+        add result, esc(chr(c1), reserved) & esc(chr(c2), reserved)
+      else: 
+        add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved)
+      c1 = c2
+    inc(c1)
+  
+proc CharSetEsc(cc: set[char]): string =
+  if card(cc) >= 128+64: 
+    result = "[^" & CharSetEscAux({'\1'..'\xFF'} - cc) & ']'
+  else: 
+    result = '[' & CharSetEscAux(cc) & ']'
+  
+proc toStrAux(r: TPeg, res: var string) = 
+  case r.kind
+  of pkEmpty: add(res, "()")
+  of pkAny: add(res, '.')
+  of pkAnyRune: add(res, '_')
+  of pkNewline: add(res, "\\n")
+  of pkTerminal: add(res, singleQuoteEsc(r.term))
+  of pkTerminalIgnoreCase:
+    add(res, 'i')
+    add(res, singleQuoteEsc(r.term))
+  of pkTerminalIgnoreStyle:
+    add(res, 'y')
+    add(res, singleQuoteEsc(r.term))
+  of pkChar: add(res, singleQuoteEsc(r.ch))
+  of pkCharChoice: add(res, charSetEsc(r.charChoice^))
+  of pkNonTerminal: add(res, r.nt.name)
+  of pkSequence:
+    add(res, '(')
+    toStrAux(r.sons[0], res)
+    for i in 1 .. high(r.sons):
+      add(res, ' ')
+      toStrAux(r.sons[i], res)
+    add(res, ')')
+  of pkOrderedChoice:
+    add(res, '(')
+    toStrAux(r.sons[0], res)
+    for i in 1 .. high(r.sons):
+      add(res, " / ")
+      toStrAux(r.sons[i], res)
+    add(res, ')')
+  of pkGreedyRep:
+    toStrAux(r.sons[0], res)
+    add(res, '*')
+  of pkGreedyRepChar:
+    add(res, singleQuoteEsc(r.ch))
+    add(res, '*')
+  of pkGreedyRepSet:
+    add(res, charSetEsc(r.charChoice^))
+    add(res, '*')
+  of pkGreedyAny:
+    add(res, ".*")
+  of pkOption:
+    toStrAux(r.sons[0], res)
+    add(res, '?')
+  of pkAndPredicate:
+    add(res, '&')
+    toStrAux(r.sons[0], res)
+  of pkNotPredicate:
+    add(res, '!')
+    toStrAux(r.sons[0], res)
+  of pkCapture:
+    add(res, '{')
+    toStrAux(r.sons[0], res)    
+    add(res, '}')
+  of pkRule:
+    toStrAux(r.sons[0], res)    
+    add(res, " <- ")
+    toStrAux(r.sons[1], res)
+  of pkList:
+    for i in 0 .. high(r.sons):
+      toStrAux(r.sons[i], res)
+      add(res, "\n")  
+
+proc `$` *(r: TPeg): string =
+  ## converts a PEG to its string representation
+  result = ""
+  toStrAux(r, result)
+
+
+# ------------------- scanner -------------------------------------------------
+
+type
+  TModifier = enum
+    modNone,
+    modVerbatim,
+    modIgnoreCase,
+    modIgnoreStyle
+  TTokKind = enum       ## enumeration of all tokens
+    tkInvalid,          ## invalid token
+    tkEof,              ## end of file reached
+    tkAny,              ## .
+    tkAnyRune,          ## _
+    tkIdentifier,       ## abc
+    tkStringLit,        ## "abc" or 'abc'
+    tkCharSet,          ## [^A-Z]
+    tkParLe,            ## '('
+    tkParRi,            ## ')'
+    tkCurlyLe,          ## '{'
+    tkCurlyRi,          ## '}'
+    tkArrow,            ## '<-'
+    tkBar,              ## '/'
+    tkStar,             ## '*'
+    tkPlus,             ## '+'
+    tkAmp,              ## '&'
+    tkNot,              ## '!'
+    tkOption,           ## '?'
+    tkBuiltin,          ## \identifier
+    tkEscaped           ## \\
+  
+  TToken {.final.} = object  ## a token
+    kind: TTokKind           ## the type of the token
+    modifier: TModifier
+    literal: string          ## the parsed (string) literal
+    charset: set[char]       ## if kind == tkCharSet
+  
+  TPegLexer = object          ## the lexer object.
+    bufpos: int               ## the current position within the buffer
+    buf: cstring              ## the buffer itself
+    LineNumber: int           ## the current line number
+    lineStart: int            ## index of last line start in buffer
+    colOffset: int            ## column to add
+    filename: string
+
+const
+  tokKindToStr: array[TTokKind, string] = [
+    "invalid", "[EOF]", ".", "_", "identifier", "string literal",
+    "character set", "(", ")", "{", "}", "<-", "/", "*", "+", "&", "!", "?",
+    "built-in", "escaped"
+  ]
+
+proc HandleCR(L: var TPegLexer, pos: int): int =
+  assert(L.buf[pos] == '\c')
+  inc(L.linenumber)
+  result = pos+1
+  if L.buf[result] == '\L': inc(result)
+  L.lineStart = result
+
+proc HandleLF(L: var TPegLexer, pos: int): int =
+  assert(L.buf[pos] == '\L')
+  inc(L.linenumber)
+  result = pos+1
+  L.lineStart = result
+
+proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) = 
+  L.buf = input
+  L.bufpos = 0
+  L.lineNumber = line
+  L.colOffset = col
+  L.lineStart = 0
+  L.filename = filename
+
+proc getColumn(L: TPegLexer): int {.inline.} = 
+  result = abs(L.bufpos - L.lineStart) + L.colOffset
+
+proc getLine(L: TPegLexer): int {.inline.} = 
+  result = L.linenumber
+  
+proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string =
+  var line = if line < 0: getLine(L) else: line
+  var col = if col < 0: getColumn(L) else: col
+  result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
+
+proc handleHexChar(c: var TPegLexer, xi: var int) = 
+  case c.buf[c.bufpos]
+  of '0'..'9': 
+    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
+    inc(c.bufpos)
+  of 'a'..'f': 
+    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
+    inc(c.bufpos)
+  of 'A'..'F': 
+    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
+    inc(c.bufpos)
+  else: nil
+
+proc getEscapedChar(c: var TPegLexer, tok: var TToken) = 
+  inc(c.bufpos)
+  case c.buf[c.bufpos]
+  of 'r', 'R', 'c', 'C': 
+    add(tok.literal, '\c')
+    Inc(c.bufpos)
+  of 'l', 'L': 
+    add(tok.literal, '\L')
+    Inc(c.bufpos)
+  of 'f', 'F': 
+    add(tok.literal, '\f')
+    inc(c.bufpos)
+  of 'e', 'E': 
+    add(tok.literal, '\e')
+    Inc(c.bufpos)
+  of 'a', 'A': 
+    add(tok.literal, '\a')
+    Inc(c.bufpos)
+  of 'b', 'B': 
+    add(tok.literal, '\b')
+    Inc(c.bufpos)
+  of 'v', 'V': 
+    add(tok.literal, '\v')
+    Inc(c.bufpos)
+  of 't', 'T': 
+    add(tok.literal, '\t')
+    Inc(c.bufpos)
+  of 'x', 'X': 
+    inc(c.bufpos)
+    var xi = 0
+    handleHexChar(c, xi)
+    handleHexChar(c, xi)
+    if xi == 0: tok.kind = tkInvalid
+    else: add(tok.literal, Chr(xi))
+  of '0'..'9': 
+    var val = ord(c.buf[c.bufpos]) - ord('0')
+    Inc(c.bufpos)
+    var i = 1
+    while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): 
+      val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
+      inc(c.bufpos)
+      inc(i)
+    if val > 0 and val <= 255: add(tok.literal, chr(val))
+    else: tok.kind = tkInvalid
+  of '\0'..'\31':
+    tok.kind = tkInvalid
+  elif c.buf[c.bufpos] in strutils.letters:
+    tok.kind = tkInvalid
+  else:
+    add(tok.literal, c.buf[c.bufpos])
+    Inc(c.bufpos)
+  
+proc skip(c: var TPegLexer) = 
+  var pos = c.bufpos
+  var buf = c.buf
+  while true: 
+    case buf[pos]
+    of ' ', '\t': 
+      Inc(pos)
+    of '#':
+      while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
+    of '\c':
+      pos = HandleCR(c, pos)
+      buf = c.buf
+    of '\L': 
+      pos = HandleLF(c, pos)
+      buf = c.buf
+    else: 
+      break                   # EndOfFile also leaves the loop
+  c.bufpos = pos
+  
+proc getString(c: var TPegLexer, tok: var TToken) = 
+  tok.kind = tkStringLit
+  var pos = c.bufPos + 1
+  var buf = c.buf
+  var quote = buf[pos-1]
+  while true: 
+    case buf[pos]
+    of '\\':
+      c.bufpos = pos
+      getEscapedChar(c, tok)
+      pos = c.bufpos
+    of '\c', '\L', '\0':
+      tok.kind = tkInvalid
+      break
+    elif buf[pos] == quote:
+      inc(pos)
+      break      
+    else:
+      add(tok.literal, buf[pos])
+      Inc(pos)
+  c.bufpos = pos
+  
+proc getCharSet(c: var TPegLexer, tok: var TToken) = 
+  tok.kind = tkCharSet
+  tok.charset = {}
+  var pos = c.bufPos + 1
+  var buf = c.buf
+  var caret = false
+  if buf[pos] == '^':
+    inc(pos)
+    caret = true
+  while true:
+    var ch: char
+    case buf[pos]
+    of ']':
+      inc(pos)
+      break
+    of '\\':
+      c.bufpos = pos
+      getEscapedChar(c, tok)
+      pos = c.bufpos
+      ch = tok.literal[tok.literal.len-1]
+    of '\C', '\L', '\0':
+      tok.kind = tkInvalid
+      break
+    else: 
+      ch = buf[pos]
+      Inc(pos)
+    incl(tok.charset, ch)
+    if buf[pos] == '-':
+      if buf[pos+1] == ']':
+        incl(tok.charset, '-')
+        inc(pos)
+      else:
+        inc(pos)
+        var ch2: char
+        case buf[pos]
+        of '\\':
+          c.bufpos = pos
+          getEscapedChar(c, tok)
+          pos = c.bufpos
+          ch2 = tok.literal[tok.literal.len-1]
+        of '\C', '\L', '\0':
+          tok.kind = tkInvalid
+          break
+        else: 
+          ch2 = buf[pos]
+          Inc(pos)
+        for i in ord(ch)+1 .. ord(ch2):
+          incl(tok.charset, chr(i))
+  c.bufpos = pos
+  if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
+  
+proc getSymbol(c: var TPegLexer, tok: var TToken) = 
+  var pos = c.bufpos
+  var buf = c.buf
+  while true: 
+    add(tok.literal, buf[pos])
+    Inc(pos)
+    if buf[pos] notin strutils.IdentChars: break
+  c.bufpos = pos
+  tok.kind = tkIdentifier
+
+proc getBuiltin(c: var TPegLexer, tok: var TToken) =
+  if c.buf[c.bufpos+1] in strutils.Letters:
+    inc(c.bufpos)
+    getSymbol(c, tok)
+    tok.kind = tkBuiltin
+  else:
+    tok.kind = tkEscaped
+    getEscapedChar(c, tok) # may set tok.kind to tkInvalid
+
+proc getTok(c: var TPegLexer, tok: var TToken) = 
+  tok.kind = tkInvalid
+  tok.modifier = modNone
+  setlen(tok.literal, 0)
+  skip(c)
+  case c.buf[c.bufpos]
+  of '{':
+    tok.kind = tkCurlyLe
+    inc(c.bufpos)
+    add(tok.literal, '{')
+  of '}': 
+    tok.kind = tkCurlyRi
+    inc(c.bufpos)
+    add(tok.literal, '}')
+  of '[': 
+    getCharset(c, tok)
+  of '(':
+    tok.kind = tkParLe
+    Inc(c.bufpos)
+    add(tok.literal, '(')
+  of ')':
+    tok.kind = tkParRi
+    Inc(c.bufpos)
+    add(tok.literal, ')')
+  of '.': 
+    tok.kind = tkAny
+    inc(c.bufpos)
+    add(tok.literal, '.')
+  of '_':
+    tok.kind = tkAnyRune
+    inc(c.bufpos)
+    add(tok.literal, '_')
+  of '\\': 
+    getBuiltin(c, tok)
+  of '\'', '"': getString(c, tok)
+  of '\0': 
+    tok.kind = tkEof
+    tok.literal = "[EOF]"
+  of 'a'..'z', 'A'..'Z', '\128'..'\255':
+    getSymbol(c, tok)
+    if c.buf[c.bufpos] in {'\'', '"'}:
+      case tok.literal
+      of "i": tok.modifier = modIgnoreCase
+      of "y": tok.modifier = modIgnoreStyle
+      of "v": tok.modifier = modVerbatim
+      else: nil
+      setLen(tok.literal, 0)
+      getString(c, tok)
+      if tok.modifier == modNone: tok.kind = tkInvalid
+  of '+':
+    tok.kind = tkPlus
+    inc(c.bufpos)
+    add(tok.literal, '+')
+  of '*':
+    tok.kind = tkStar
+    inc(c.bufpos)
+    add(tok.literal, '+')
+  of '<':
+    if c.buf[c.bufpos+1] == '-':
+      inc(c.bufpos, 2)
+      tok.kind = tkArrow
+      add(tok.literal, "<-")
+    else:
+      add(tok.literal, '<')
+  of '/':
+    tok.kind = tkBar
+    inc(c.bufpos)
+    add(tok.literal, '/')
+  of '?':
+    tok.kind = tkOption
+    inc(c.bufpos)
+    add(tok.literal, '?')
+  of '!':
+    tok.kind = tkNot
+    inc(c.bufpos)
+    add(tok.literal, '!')
+  of '&':
+    tok.kind = tkAmp
+    inc(c.bufpos)
+    add(tok.literal, '!')
+  else:
+    add(tok.literal, c.buf[c.bufpos])
+    inc(c.bufpos)
+
+proc arrowIsNextTok(c: TPegLexer): bool =
+  # the only look ahead we need
+  var pos = c.bufpos
+  while c.buf[pos] in {'\t', ' '}: inc(pos)
+  result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
+
+# ----------------------------- parser ----------------------------------------
+    
+type
+  EInvalidPeg* = object of EBase ## raised if an invalid PEG has been detected
+  TPegParser = object of TPegLexer ## the PEG parser object
+    tok: TToken
+    nonterms: seq[PNonTerminal]
+    modifier: TModifier
+
+proc getTok(p: var TPegParser) = getTok(p, p.tok)
+
+proc pegError(p: TPegParser, msg: string, line = -1, col = -1) =
+  var e: ref EInvalidPeg
+  new(e)
+  e.msg = errorStr(p, msg, line, col)
+  raise e
+
+proc eat(p: var TPegParser, kind: TTokKind) =
+  if p.tok.kind == kind: getTok(p)
+  else: pegError(p, tokKindToStr[kind] & " expected")
+
+proc parseExpr(p: var TPegParser): TPeg
+
+proc getNonTerminal(p: TPegParser, name: string): PNonTerminal =
+  for i in 0..high(p.nonterms):
+    result = p.nonterms[i]
+    if cmpIgnoreStyle(result.name, name) == 0: return
+  # forward reference:
+  result = newNonTerminal(name, getLine(p), getColumn(p))
+  add(p.nonterms, result)
+
+proc modifiedTerm(s: string, m: TModifier): TPeg =
+  case m
+  of modNone, modVerbatim: result = term(s)
+  of modIgnoreCase: result = termIgnoreCase(s)
+  of modIgnoreStyle: result = termIgnoreStyle(s)
+
+proc primary(p: var TPegParser): TPeg =
+  case p.tok.kind
+  of tkAmp:
+    getTok(p)
+    return &primary(p)
+  of tkNot:
+    getTok(p)
+    return !primary(p)
+  else: nil
+  case p.tok.kind
+  of tkIdentifier:
+    if not arrowIsNextTok(p):
+      var nt = getNonTerminal(p, p.tok.literal)
+      incl(nt.flags, ntUsed)
+      result = nonTerminal(nt)
+      getTok(p)
+    else:
+      pegError(p, "expression expected, but found: " & p.tok.literal)
+  of tkStringLit:
+    var m = p.tok.modifier
+    if m == modNone: m = p.modifier
+    result = modifiedTerm(p.tok.literal, m)
+    getTok(p)
+  of tkCharSet:
+    if '\0' in p.tok.charset:
+      pegError(p, "binary zero ('\\0') not allowed in character class")
+    result = charset(p.tok.charset)
+    getTok(p)
+  of tkParLe:
+    getTok(p)
+    result = parseExpr(p)
+    eat(p, tkParRi)
+  of tkCurlyLe:
+    getTok(p)
+    result = capture(parseExpr(p))
+    eat(p, tkCurlyRi)
+  of tkAny:
+    result = any()
+    getTok(p)
+  of tkAnyRune:
+    result = anyRune()
+    getTok(p)
+  of tkBuiltin:
+    case p.tok.literal
+    of "n": result = newLine()
+    of "d": result = charset({'0'..'9'})
+    of "D": result = charset({'\1'..'\xff'} - {'0'..'9'})
+    of "s": result = charset({' ', '\9'..'\13'})
+    of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
+    of "w": result = charset({'a'..'z', 'A'..'Z', '_'})
+    of "W": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z', '_'})
+    of "ident": result = pegs.ident
+    else: pegError(p, "unknown built-in: " & p.tok.literal)
+    getTok(p)
+  of tkEscaped:
+    result = term(p.tok.literal[0])
+    getTok(p)
+  else:
+    pegError(p, "expression expected, but found: " & p.tok.literal)
+    getTok(p) # we must consume a token here to prevent endless loops!
+  while true:
+    case p.tok.kind
+    of tkOption:
+      result = ?result
+      getTok(p)
+    of tkStar:
+      result = *result
+      getTok(p)
+    of tkPlus:
+      result = +result
+      getTok(p)
+    else: break
+
+proc seqExpr(p: var TPegParser): TPeg =
+  result = primary(p)
+  while true:
+    case p.tok.kind
+    of tkAmp, tkNot, tkStringLit, tkCharset, tkParLe, tkCurlyLe,
+       tkAny, tkAnyRune, tkBuiltin, tkEscaped:
+      result = sequence(result, primary(p))
+    of tkIdentifier:
+      if not arrowIsNextTok(p):
+        result = sequence(result, primary(p))
+      else: break
+    else: break
+
+proc parseExpr(p: var TPegParser): TPeg =
+  result = seqExpr(p)
+  while p.tok.kind == tkBar:
+    getTok(p)
+    result = result / seqExpr(p)
+  
+proc parseRule(p: var TPegParser): PNonTerminal =
+  if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
+    result = getNonTerminal(p, p.tok.literal)
+    if ntDeclared in result.flags:
+      pegError(p, "attempt to redefine: " & result.name)
+    result.line = getLine(p)
+    result.col = getColumn(p)
+    getTok(p)
+    eat(p, tkArrow)
+    result.rule = parseExpr(p)
+    incl(result.flags, ntDeclared) # NOW inlining may be attempted
+  else:
+    pegError(p, "rule expected, but found: " & p.tok.literal)
+  
+proc rawParse(p: var TPegParser): TPeg =
+  ## parses a rule or a PEG expression
+  if p.tok.kind == tkBuiltin:
+    case p.tok.literal
+    of "i":
+      p.modifier = modIgnoreCase
+      getTok(p)
+    of "y":
+      p.modifier = modIgnoreStyle
+      getTok(p)
+    else: nil
+  if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
+    result = parseRule(p).rule
+    while p.tok.kind != tkEof:
+      discard parseRule(p)
+  else:
+    result = parseExpr(p)
+  if p.tok.kind != tkEof:
+    pegError(p, "EOF expected, but found: " & p.tok.literal)
+  for i in 0..high(p.nonterms):
+    var nt = p.nonterms[i]
+    if ntDeclared notin nt.flags:
+      pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col)
+    elif ntUsed notin nt.flags and i > 0:
+      pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
+
+proc parsePeg*(input: string, filename = "pattern", line = 1, col = 0): TPeg =
+  var p: TPegParser
+  init(TPegLexer(p), input, filename, line, col)
+  p.tok.kind = tkInvalid
+  p.tok.modifier = modNone
+  p.tok.literal = ""
+  p.tok.charset = {}
+  p.nonterms = @[]
+  getTok(p)
+  result = rawParse(p)
+
+proc peg*(pattern: string): TPeg =
+  ## constructs a TPeg object from the `pattern`. The short name has been
+  ## chosen to encourage its use as a raw string modifier::
+  ##
+  ##   peg"{\ident} \s* '=' \s* {.*}"
+  result = parsePeg(pattern, "pattern")
+
+when isMainModule:
+  assert match("W_HI_Le", peg"\y 'while'")
+  assert(not match("W_HI_L", peg"\y 'while'"))
+  assert(not match("W_HI_Le", peg"\y v'while'"))
+  assert match("W_HI_Le", peg"y'while'")
+  
+  assert($ +digits == $peg"\d+")
+  assert "0158787".match(peg"\d+")
+  assert "ABC 0232".match(peg"\w+\s+\d+")
+  assert "ABC".match(peg"\d+ / \w+")
+
+  for word in split("00232this02939is39an22example111", peg"\d+"):
+    writeln(stdout, word)
+
+  assert matchLen("key", ident) == 3
+
+  var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
+  assert matchLen("key1=  cal9", pattern) == 11
+  
+  var ws = newNonTerminal("ws", 1, 1)
+  ws.rule = *whitespace
+  
+  var expr = newNonTerminal("expr", 1, 1)
+  expr.rule = sequence(capture(ident), *sequence(
+                nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
+  
+  var c: TMatchClosure
+  var s = "a+b +  c +d+e+f"
+  assert m(s, expr.rule, 0, c) == len(s)
+  var a = ""
+  for i in 0..c.ml-1:
+    a.add(copy(s, c.matches[i][0], c.matches[i][1]))
+  assert a == "abcdef"
+  #echo expr.rule
+
+  #const filename = "lib/devel/peg/grammar.txt"
+  #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
+  #echo "a <- [abc]*?".match(grammar)
+  assert find("_____abc_______", term("abc")) == 5
+  assert match("_______ana", peg"A <- 'ana' / . A")
+  assert match("abcs%%%", peg"A <- ..A / .A / '%'")
+
+  if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
+    assert matches[0] == "abc"
+  else:
+    assert false
+  
+  var g2 = peg"""S <- A B / C D
+                 A <- 'a'+
+                 B <- 'b'+
+                 C <- 'c'+
+                 D <- 'd'+
+              """
+  assert($g2 == "((A B) / (C D))")
+  assert match("cccccdddddd", g2)
+  echo "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
+  assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
+
diff --git a/lib/pure/re.nim b/lib/pure/re.nim
new file mode 100755
index 000000000..f854c07e5
--- /dev/null
+++ b/lib/pure/re.nim
@@ -0,0 +1,353 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Regular expression support for Nimrod. Consider using the pegs module
+## instead.
+
+{.compile: "tre/tre_all.c".}
+
+from strutils import addf
+
+type
+  TRegExDesc {.pure, final.} = object
+    re_nsub: int # Number of parenthesized subexpressions.
+    value: pointer  # For internal use only.
+  
+  TRegEx* = ref TRegExDesc ## a compiled regular expression  
+  EInvalidRegEx* = object of EInvalidValue
+    ## is raised if the pattern is no valid regular expression.
+    
+  TRegMatch {.pure.} = object
+    so, eo: cint
+
+const
+  MaxSubpatterns* = 10
+    ## defines the maximum number of subpatterns that can be captured.
+    ## More subpatterns cannot be captured!
+
+proc regnexec(preg: ptr TRegExDesc, s: cstring, len, nmatch: int,
+              pmatch: ptr array [0..maxSubpatterns-1, TRegMatch],
+              eflags: cint): cint {.importc.}
+proc regncomp(preg: ptr TRegExDesc, regex: cstring, n: int,
+              cflags: cint): cint {.importc.}
+proc regfree(preg: ptr TRegExDesc) {.importc.}
+
+const
+  # POSIX regcomp() flags
+  REG_EXTENDED = 1
+  REG_ICASE = (REG_EXTENDED shl 1)
+  REG_NEWLINE = (REG_ICASE shl 1)
+  REG_NOSUB = (REG_NEWLINE shl 1)
+  # Extra regcomp() flags
+  REG_BASIC = 0
+  REG_LITERAL = (REG_NOSUB shl 1)
+  REG_RIGHT_ASSOC = (REG_LITERAL shl 1)
+  REG_UNGREEDY = (REG_RIGHT_ASSOC shl 1)
+
+  # POSIX regexec() flags
+  REG_NOTBOL = 1
+  REG_NOTEOL = (REG_NOTBOL shl 1)
+
+  # Extra regexec() flags
+  REG_APPROX_MATCHER = (REG_NOTEOL shl 1)
+  REG_BACKTRACKING_MATCHER = (REG_APPROX_MATCHER shl 1)
+
+  ErrorMessages = [
+    "No error",
+    "No match",
+    "Invalid regexp",
+    "Unknown collating element",
+    "Unknown character class name",
+    "Trailing backslash",
+    "Invalid back reference",
+    "Missing ']'",
+    "Missing ')'",
+    "Missing '}'",
+    "Invalid contents of {}",
+    "Invalid character range",
+    "Out of memory",
+    "Invalid use of repetition operators"
+  ]
+
+proc finalizeRegEx(x: TRegEx) = regfree(addr(x^))
+
+proc re*(s: string): TRegEx =
+  ## Constructor of regular expressions. Note that Nimrod's
+  ## extended raw string literals supports this syntax ``re"[abc]"`` as
+  ## a short form for ``re(r"[abc]")``.
+  new(result, finalizeRegEx)
+  var err = int(regncomp(addr(result^), s, s.len,
+                cint(REG_EXTENDED or REG_NEWLINE)))
+  if err != 0:
+    var e: ref EInvalidRegEx
+    new(e)
+    e.msg = ErrorMessages[err]
+    raise e
+
+proc xre*(pattern: string): TRegEx = 
+  ## deletes whitespace from a pattern that is not escaped or in a character
+  ## class. Then it constructs a regular expresion object via `re`.
+  ## This is modelled after Perl's ``/x`` modifier. 
+  var p = ""
+  var i = 0
+  while i < pattern.len: 
+    case pattern[i]
+    of ' ', '\t': 
+      inc i
+    of '\\': 
+      add p, '\\'
+      add p, pattern[i+1]
+      inc i, 2
+    of '[': 
+      while pattern[i] != ']' and pattern[i] != '\0': 
+        add p, pattern[i]
+        inc i
+    else: 
+      add p, pattern[i]
+      inc i
+  result = re(p)
+
+proc rawmatch(s: string, pattern: TRegEx, matches: var openarray[string],
+              start: int): tuple[first, last: int] =
+  var
+    rawMatches: array [0..maxSubpatterns-1, TRegMatch]
+    cs = cstring(s)
+    res = int(regnexec(addr(pattern^), cast[cstring](addr(cs[start])),
+              s.len-start, maxSubpatterns, addr(rawMatches), cint(0)))
+  if res == 0:
+    for i in 0..min(matches.len, int(pattern.re_nsub))-1:
+      var
+        a = int(rawMatches[i].so)
+        b = int(rawMatches[i].eo)
+      echo "a: ", a, " b: ", b
+      if a >= 0 and b >= 0:
+        matches[i] = copy(s, a+start, b - 1 + start)
+      else:
+        matches[i] = ""
+    return (int(rawMatches[0].so), int(rawMatches[0].eo)-1)
+  return (-1, -1)
+
+proc match*(s: string, pattern: TRegEx, matches: var openarray[string],
+            start = 0): bool =
+  ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
+  ## the captured substrings in the array ``matches``. If it does not
+  ## match, nothing is written into ``matches`` and ``false`` is
+  ## returned.
+  result = rawmatch(s, pattern, matches, start).first == 0
+
+proc match*(s: string, pattern: TRegEx, start: int = 0): bool =
+  ## returns ``true`` if ``s`` matches the ``pattern`` beginning
+  ## from ``start``.
+  var matches: array [0..0, string]
+  result = rawmatch(s, pattern, matches, start).first == 0
+
+proc matchLen*(s: string, pattern: TRegEx, matches: var openarray[string],
+               start = 0): int =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, -1 is returned. Note that a match length
+  ## of zero can happen.
+  var (a, b) = rawmatch(s, pattern, matches, start)
+  result = a - b + 1
+
+proc matchLen*(s: string, pattern: TRegEx, start = 0): int =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, -1 is returned. Note that a match length
+  ## of zero can happen.
+  var matches: array [0..0, string]
+  var (a, b) = rawmatch(s, pattern, matches, start)
+  result = a - b + 1
+
+proc find*(s: string, pattern: TRegEx, matches: var openarray[string],
+           start = 0): int =
+  ## returns ``true`` if ``pattern`` occurs in ``s`` and the captured
+  ## substrings in the array ``matches``. If it does not match, nothing
+  ## is written into ``matches``.
+  result = rawmatch(s, pattern, matches, start).first
+  if result >= 0: inc(result, start)
+
+proc find*(s: string, pattern: TRegEx, start = 0): int =
+  ## returns ``true`` if ``pattern`` occurs in ``s``.
+  var matches: array [0..0, string]
+  result = rawmatch(s, pattern, matches, start).first
+  if result >= 0: inc(result, start)
+
+template `=~`*(s: string, pattern: TRegEx): expr = 
+  ## This calls ``match`` with an implicit declared ``matches`` array that 
+  ## can be used in the scope of the ``=~`` call: 
+  ## 
+  ## .. code-block:: nimrod
+  ##
+  ##   if line =~ r"\s*(\w+)\s*\=\s*(\w+)": 
+  ##     # matches a key=value pair:
+  ##     echo("Key: ", matches[1])
+  ##     echo("Value: ", matches[2])
+  ##   elif line =~ r"\s*(\#.*)":
+  ##     # matches a comment
+  ##     # note that the implicit ``matches`` array is different from the
+  ##     # ``matches`` array of the first branch
+  ##     echo("comment: ", matches[1])
+  ##   else:
+  ##     echo("syntax error")
+  ##
+  when not definedInScope(matches):
+    var matches: array[0..maxSubPatterns-1, string]
+  match(s, pattern, matches)
+  
+# ------------------------- more string handling ------------------------------
+
+proc contains*(s: string, pattern: TRegEx, start = 0): bool =
+  ## same as ``find(s, pattern, start) >= 0``
+  return find(s, pattern, start) >= 0
+
+proc contains*(s: string, pattern: TRegEx, matches: var openArray[string],
+              start = 0): bool =
+  ## same as ``find(s, pattern, matches, start) >= 0``
+  return find(s, pattern, matches, start) >= 0
+
+proc startsWith*(s: string, prefix: TRegEx): bool =
+  ## returns true if `s` starts with the pattern `prefix`
+  result = matchLen(s, prefix) >= 0
+
+proc endsWith*(s: string, suffix: TRegEx): bool =
+  ## returns true if `s` ends with the pattern `prefix`
+  for i in 0 .. s.len-1:
+    if matchLen(s, suffix, i) == s.len - i: return true
+
+proc replace*(s: string, sub: TRegEx, by: string): string =
+  ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
+  ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
+  ##
+  ## .. code-block:: nimrod
+  ##   "var1=key; var2=key2".replace(p"{\ident}'='{\ident}", "$1<-$2$2")
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##   "var1<-keykey; val2<-key2key2"
+  result = ""
+  var i = 0
+  var caps: array[0..maxSubpatterns-1, string]
+  while i < s.len:
+    var x = matchLen(s, sub, caps, i)
+    if x <= 0:
+      add(result, s[i])
+      inc(i)
+    else:
+      addf(result, by, caps)
+      inc(i, x)
+  # copy the rest:
+  add(result, copy(s, i))
+  
+proc parallelReplace*(s: string, subs: openArray[
+                      tuple[pattern: TRegEx, repl: string]]): string = 
+  ## Returns a modified copy of `s` with the substitutions in `subs`
+  ## applied in parallel.
+  result = ""
+  var i = 0
+  var caps: array[0..maxSubpatterns-1, string]
+  while i < s.len:
+    block searchSubs:
+      for j in 0..high(subs):
+        var x = matchLen(s, subs[j][0], caps, i)
+        if x > 0:
+          addf(result, subs[j][1], caps)
+          inc(i, x)
+          break searchSubs
+      add(result, s[i])
+      inc(i)
+  # copy the rest:
+  add(result, copy(s, i))  
+  
+proc transformFile*(infile, outfile: string,
+                    subs: openArray[tuple[pattern: TRegEx, repl: string]]) =
+  ## reads in the file `infile`, performs a parallel replacement (calls
+  ## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
+  ## error occurs. This is supposed to be used for quick scripting.
+  var x = readFile(infile)
+  if not isNil(x):
+    var f: TFile
+    if open(f, outfile, fmWrite):
+      write(f, x.parallelReplace(subs))
+      close(f)
+    else:
+      quit("cannot open for writing: " & outfile)
+  else:
+    quit("cannot open for reading: " & infile)
+  
+iterator split*(s: string, sep: TRegEx): string =
+  ## Splits the string `s` into substrings.
+  ##
+  ## Substrings are separated by the regular expression `sep`.
+  ## Examples:
+  ##
+  ## .. code-block:: nimrod
+  ##   for word in split("00232this02939is39an22example111", re"\d+"):
+  ##     writeln(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nimrod
+  ##   "this"
+  ##   "is"
+  ##   "an"
+  ##   "example"
+  ##
+  var
+    first = 0
+    last = 0
+  while last < len(s):
+    var x = matchLen(s, sep, last)
+    if x > 0: inc(last, x)
+    first = last
+    while last < len(s):
+      inc(last)
+      x = matchLen(s, sep, last)
+      if x > 0: break
+    if first < last:
+      yield copy(s, first, last-1)
+
+proc split*(s: string, sep: TRegEx): seq[string] =
+  ## Splits the string `s` into substrings.
+  accumulateResult(split(s, sep))
+  
+const ## common regular expressions
+  reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"  ## describes an identifier
+  reNatural* = r"\b\d+\b" ## describes a natural number
+  reInteger* = r"\b[-+]?\d+\b" ## describes an integer
+  reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
+  reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
+  reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
+  reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
+    ## describes a floating point number
+  reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
+             r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
+             r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
+    ## describes a common email address
+  reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
+           r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
+    ## describes an URL
+
+when isMainModule:
+  echo matchLen("key", re"[a-zA-Z_][a-zA-Z_0-9]*")
+
+  var pattern = re"[a-zA-Z_][a-zA-Z_0-9]*\s*=\s*[a-zA-Z_][a-zA-Z_0-9]*"
+  echo matchLen("key1=  cal9", pattern, 2)
+
+  echo find("_____abc_______", re("abc"), 3)
+  #echo "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
+  #echo "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
+
+  if "abc" =~ re"(a)bc xyz|([a-z]+)":
+    echo matches[0]
+  else:
+    echo "BUG"
+
+#  for word in split("00232this02939is39an22example111", peg"\d+"):
+#    writeln(stdout, word)
diff --git a/lib/pure/regexprs.nim b/lib/pure/regexprs.nim
index 54f9dc471..cff3152cf 100755
--- a/lib/pure/regexprs.nim
+++ b/lib/pure/regexprs.nim
@@ -8,9 +8,6 @@
 #
 
 ## Regular expression support for Nimrod.
-## **Deprecated** since version 0.8.2. Use the module `re` instead.
-{.deprecated.}
-
 ## Currently this module is implemented by providing a wrapper around the
 ## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
 ## C library. This means that your application will depend on the PRCE
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 337be31d1..f4d2911fc 100755
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -48,7 +48,8 @@ proc read[T](s: PStream, result: var T) =
 
 proc readChar*(s: PStream): char =
   ## reads a char from the stream `s`. Raises `EIO` if an error occured.
-  read(s, result)
+  ## Returns '\0' as an EOF marker.
+  discard s.readData(s, addr(result), sizeof(result))
 
 proc readBool*(s: PStream): bool = 
   ## reads a bool from the stream `s`. Raises `EIO` if an error occured.
@@ -94,7 +95,7 @@ proc readLine*(s: PStream): string =
     if c == '\c': 
       c = readChar(s)
       break
-    elif c == '\L': break
+    elif c == '\L' or c == '\0': break
     result.add(c)
 
 type
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index cfbfef810..25b495f10 100755
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -45,10 +45,6 @@ const
   IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
     ## the set of characters an identifier can start with
 
-#  strStart* = 0 ## this is only for bootstraping
-#                ## XXX: remove this someday
-#  nl* = "\n"    ## this is only for bootstraping XXX: remove this someday
-
 proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
   ## The `substitution`:idx: operator performs string substitutions in
   ## `formatstr` and returns a modified `formatstr`. This is often called
@@ -98,51 +94,30 @@ proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
   ## If `leading` is true, leading whitespace is stripped.
   ## If `trailing` is true, trailing whitespace is stripped.
 
-proc toLower*(s: string): string {.noSideEffect.}
+proc toLower*(s: string): string {.noSideEffect, procvar.}
   ## Converts `s` into lower case. This works only for the letters A-Z.
   ## See `unicode.toLower` for a version that works for any Unicode character.
 
-proc toLower*(c: Char): Char {.noSideEffect.}
+proc toLower*(c: Char): Char {.noSideEffect, procvar.}
   ## Converts `c` into lower case. This works only for the letters A-Z.
   ## See `unicode.toLower` for a version that works for any Unicode character.
 
-proc toUpper*(s: string): string {.noSideEffect.}
+proc toUpper*(s: string): string {.noSideEffect, procvar.}
   ## Converts `s` into upper case. This works only for the letters a-z.
   ## See `unicode.toUpper` for a version that works for any Unicode character.
 
-proc toUpper*(c: Char): Char {.noSideEffect.}
+proc toUpper*(c: Char): Char {.noSideEffect, procvar.}
   ## Converts `c` into upper case. This works only for the letters a-z.
   ## See `unicode.toUpper` for a version that works for any Unicode character.
 
-proc capitalize*(s: string): string {.noSideEffect.}
+proc capitalize*(s: string): string {.noSideEffect, procvar.}
   ## Converts the first character of `s` into upper case.
   ## This works only for the letters a-z.
 
-proc normalize*(s: string): string {.noSideEffect.}
+proc normalize*(s: string): string {.noSideEffect, procvar.}
   ## Normalizes the string `s`. That means to convert it to lower case and
   ## remove any '_'. This is needed for Nimrod identifiers for example.
 
-proc findSubStr*(sub, s: string, start: int = 0): int {.
-  noSideEffect, deprecated.}
-  ## Searches for `sub` in `s` starting at position `start`. Searching is
-  ## case-sensitive. If `sub` is not in `s`, -1 is returned.
-  ## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
-  ## this has a different parameter order.
-
-proc findSubStr*(sub: char, s: string, start: int = 0): int {.
-  noSideEffect, deprecated.}
-  ## Searches for `sub` in `s` starting at position `start`. Searching is
-  ## case-sensitive. If `sub` is not in `s`, -1 is returned.
-  ## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
-  ## this has a different parameter order.
-
-proc findChars*(chars: set[char], s: string, start: int = 0): int {.
-  noSideEffect, deprecated.}
-  ## Searches for `chars` in `s` starting at position `start`. If `s` contains
-  ## none of the characters in `chars`, -1 is returned.
-  ## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
-  ## this has a different parameter order.
-
 proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
   ## Searches for `sub` in `s` starting at position `start`. Searching is
   ## case-sensitive. If `sub` is not in `s`, -1 is returned.
@@ -322,13 +297,13 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
   ## | > 0 iff a > b
 
 proc contains*(s: string, c: char): bool {.noSideEffect.}
-  ## Same as ``findSubStr(c, s) >= 0``.
+  ## Same as ``find(s, c) >= 0``.
 
 proc contains*(s, sub: string): bool {.noSideEffect.}
-  ## Same as ``findSubStr(sub, s) >= 0``.
+  ## Same as ``find(s, sub) >= 0``.
 
 proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
-  ## Same as ``findChars(s, chars) >= 0``.
+  ## Same as ``find(s, chars) >= 0``.
 
 proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
   ## Converts `x` to its hexadecimal representation. The resulting string
@@ -340,15 +315,15 @@ proc intToStr*(x: int, minchars: int = 1): string
   ## will be minimally `minchars` characters long. This is achieved by
   ## adding leading zeros.
 
-proc ParseInt*(s: string): int {.noSideEffect.}
+proc ParseInt*(s: string): int {.noSideEffect, procvar.}
   ## Parses a decimal integer value contained in `s`. If `s` is not
   ## a valid integer, `EInvalidValue` is raised.
 
-proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.}
+proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar.}
   ## Parses a decimal integer value contained in `s`. If `s` is not
   ## a valid integer, `EInvalidValue` is raised.
 
-proc ParseFloat*(s: string): float {.noSideEffect.}
+proc ParseFloat*(s: string): float {.noSideEffect, procvar.}
   ## Parses a decimal floating point value contained in `s`. If `s` is not
   ## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
   ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
@@ -616,7 +591,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
   for i in 0..0xff: a[chr(i)] = m+1
   for i in 0..m-1: a[sub[i]] = m-i
 
-proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
+proc findAux(s, sub: string, start: int, a: TSkipTable): int =
   # fast "quick search" algorithm:
   var
     m = len(sub)
@@ -631,36 +606,10 @@ proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
     inc(j, a[s[j+m]])
   return -1
 
-proc findSubStr(sub, s: string, start: int = 0): int =
-  var a: TSkipTable
-  preprocessSub(sub, a)
-  result = findSubStrAux(s, sub, start, a)
-  # slow linear search:
-  #var
-  #  i, j, M, N: int
-  #M = len(sub)
-  #N = len(s)
-  #i = start
-  #j = 0
-  #if i >= N:
-  #  result = -1
-  #else:
-  #  while True:
-  #    if s[i] == sub[j]:
-  #      Inc(i)
-  #      Inc(j)
-  #    else:
-  #      i = i - j + 1
-  #      j = 0
-  #    if (j >= M):
-  #      return i - M
-  #    elif (i >= N):
-  #      return -1
-
 proc find(s, sub: string, start: int = 0): int =
   var a: TSkipTable
   preprocessSub(sub, a)
-  result = findSubStrAux(s, sub, start, a)
+  result = findAux(s, sub, start, a)
 
 proc find(s: string, sub: char, start: int = 0): int =
   for i in start..len(s)-1:
@@ -672,16 +621,6 @@ proc find(s: string, chars: set[char], start: int = 0): int =
     if s[i] in chars: return i
   return -1 
 
-proc findSubStr(sub: char, s: string, start: int = 0): int =
-  for i in start..len(s)-1:
-    if sub == s[i]: return i
-  return -1
- 
-proc findChars(chars: set[char], s: string, start: int = 0): int =
-  for i in start..s.len-1:
-    if s[i] in chars: return i
-  return -1
-  
 proc contains(s: string, chars: set[char]): bool =
   return find(s, chars) >= 0
 
@@ -698,7 +637,7 @@ proc replace*(s, sub, by: string): string =
   preprocessSub(sub, a)
   var i = 0
   while true:
-    var j = findSubStrAux(s, sub, i, a)
+    var j = findAux(s, sub, i, a)
     if j < 0: break
     add result, copy(s, i, j - 1)
     add result, by
diff --git a/lib/pure/variants.nim b/lib/pure/variants.nim
new file mode 100755
index 000000000..f661f81a6
--- /dev/null
+++ b/lib/pure/variants.nim
@@ -0,0 +1,181 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements Nimrod's support for the ``variant`` datatype.
+## `TVariant` shows how the flexibility of dynamic typing is achieved
+## within a static type system. 
+
+type
+  TVarType* = enum
+    vtNone,
+    vtBool, 
+    vtChar,
+    vtEnum,
+    vtInt,
+    vtFloat,
+    vtString,
+    vtSet,
+    vtSeq,
+    vtDict
+  TVariant* {.final.} = object of TObject
+    case vtype: TVarType
+    of vtNone: nil
+    of vtBool, vtChar, vtEnum, vtInt: vint: int64
+    of vtFloat: vfloat: float64
+    of vtString: vstring: string
+    of vtSet, vtSeq: q: seq[TVariant]
+    of vtDict: d: seq[tuple[key, val: TVariant]]
+    
+iterator objectFields*[T](x: T, skipInherited: bool): tuple[
+  key: string, val: TVariant] {.magic: "ObjectFields"}
+
+proc `<>`*(x: ordinal): TVariant =
+  result.kind = vtEnum
+  result.vint = x
+
+proc `<>`*(x: biggestInt): TVariant =
+  result.kind = vtInt
+  result.vint = x
+
+proc `<>`*(x: char): TVariant =
+  result.kind = vtChar
+  result.vint = ord(x)
+
+proc `<>`*(x: bool): TVariant =
+  result.kind = vtBool
+  result.vint = ord(x)
+
+proc `<>`*(x: biggestFloat): TVariant =
+  result.kind = vtFloat
+  result.vfloat = x
+
+proc `<>`*(x: string): TVariant =
+  result.kind = vtString
+  result.vstring = x
+
+proc `<>`*[T](x: openArray[T]): TVariant =
+  result.kind = vtSeq
+  newSeq(result.q, x.len)
+  for i in 0..x.len-1: result.q[i] = <>x[i]
+
+proc `<>`*[T](x: set[T]): TVariant =
+  result.kind = vtSet
+  result.q = @[]
+  for a in items(x): result.q.add(<>a)
+
+proc `<>`* [T: object](x: T): TVariant {.magic: "ToVariant".}
+  ## this converts a value to a variant ("boxing")
+
+proc `><`*[T](v: TVariant, typ: T): T {.magic: "FromVariant".}
+
+[<>5, <>67, <>"hallo"]
+myVar><int
+
+  
+proc `==`* (x, y: TVariant): bool =
+  if x.vtype == y.vtype:
+    case x.vtype
+    of vtNone: result = true
+    of vtBool, vtChar, vtEnum, vtInt: result = x.vint == y.vint
+    of vtFloat: result = x.vfloat == y.vfloat
+    of vtString: result = x.vstring == y.vstring
+    of vtSet:
+      # complicated! We check that each a in x also occurs in y and that the
+      # counts are identical:
+      if x.q.len == y.q.len:
+        for a in items(x.q):
+          block inner:
+            for b in items(y.q):
+              if a == b: break inner
+            return false
+        result = true
+    of vtSeq:
+      if x.q.len == y.q.len:
+        for i in 0..x.q.len-1:
+          if x.q[i] != y.q[i]: return false
+        result = true
+    of vtDict:
+      # it is an ordered dict:
+      if x.d.len == y.d.len:
+        for i in 0..x.d.len-1:
+          if x.d[i].key != y.d[i].key: return false
+          if x.d[i].val != y.d[i].val: return false
+        result = true
+
+proc `[]`* (a, b: TVariant): TVariant =
+  case a.vtype
+  of vtSeq:
+    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
+      result = a.q[b.vint]
+    else:
+      variantError()
+  of vtDict:
+    for i in 0..a.d.len-1:
+      if a.d[i].key == b: return a.d[i].val
+    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
+      result = a.d[b.vint].val
+    variantError()
+  else: variantError()
+
+proc `[]=`* (a, b, c: TVariant) =
+  case a.vtype
+  of vtSeq:
+    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
+      a.q[b.vint] = b
+    else:
+      variantError()
+  of vtDict:
+    for i in 0..a.d.len-1:
+      if a.d[i].key == b:
+        a.d[i].val = c
+        return
+    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
+      a.d[b.vint].val = c
+    variantError()
+  else: variantError()
+  
+proc `[]`* (a: TVariant, b: int): TVariant {.inline} = return a[<>b]
+proc `[]`* (a: TVariant, b: string): TVariant {.inline} = return a[<>b]
+proc `[]=`* (a: TVariant, b: int, c: TVariant) {.inline} = a[<>b] = c
+proc `[]=`* (a: TVariant, b: string, c: TVariant) {.inline} = a[<>b] = c
+
+proc `+`* (x, y: TVariant): TVariant =
+  case x.vtype
+  of vtBool, vtChar, vtEnum, vtInt:
+    if y.vtype == x.vtype:
+      result.vtype = x.vtype
+      result.vint = x.vint + y.vint
+    else:
+      case y.vtype
+      of vtBool, vtChar, vtEnum, vtInt:
+        
+    
+    
+    vint: int64
+  of vtFloat: vfloat: float64
+  of vtString: vstring: string
+  of vtSet, vtSeq: q: seq[TVariant]
+  of vtDict: d: seq[tuple[key, val: TVariant]]
+
+proc `-`* (x, y: TVariant): TVariant
+proc `*`* (x, y: TVariant): TVariant
+proc `/`* (x, y: TVariant): TVariant
+proc `div`* (x, y: TVariant): TVariant
+proc `mod`* (x, y: TVariant): TVariant
+proc `&`* (x, y: TVariant): TVariant
+proc `$`* (x: TVariant): string =
+  # uses JS notation
+  
+proc parseVariant*(s: string): TVariant
+proc `<`* (x, y: TVariant): bool
+proc `<=`* (x, y: TVariant): bool
+
+proc hash*(x: TVariant): int =
+  
+
diff --git a/lib/system.nim b/lib/system.nim
index f52a233f8..9a64362c5 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -668,7 +668,7 @@ else:
     """
 
 proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
-proc add *[T](x: var seq[T], y: seq[T]) {.noSideEffect.} =
+proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
   ## Generic proc for adding a data item `y` to a container `x`.
   ## For containers that have an order, `add` means *append*. New generic
   ## containers should also call their adding proc `add` for consistency.
@@ -721,7 +721,8 @@ type # these work for most platforms:
     ## This C type is not supported by Nimrod's code generator
 
   cstringArray* {.importc: "char**", nodecl.} = ptr array [0..50_000, cstring]
-    ## This is the same as the type ``char**`` in *C*.
+    ## This is binary compatible to the type ``char**`` in *C*. The array's
+    ## high value is large enough to disable bounds checking in practice.
 
   TEndian* = enum ## is a type describing the endianness of a processor.
     littleEndian, bigEndian
@@ -1072,28 +1073,25 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
 # This is an undocumented pragma that can only be used
 # once in the system module.
 
-proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
+proc `&` *[T](x, y: openArray[T]): seq[T] {.noSideEffect.} =
   newSeq(result, x.len + y.len)
   for i in 0..x.len-1:
     result[i] = x[i]
   for i in 0..y.len-1:
-    result[i] = y[i]
+    result[i+x.len] = y[i]
 
-proc `&` *[T](x: seq[T], y: T): seq[T] {.noSideEffect.} =
+proc `&` *[T](x: openArray[T], y: T): seq[T] {.noSideEffect.} =
   newSeq(result, x.len + 1)
   for i in 0..x.len-1:
     result[i] = x[i]
   result[x.len] = y
 
-proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} =
+proc `&` *[T](x: T, y: openArray[T]): seq[T] {.noSideEffect.} =
   newSeq(result, y.len + 1)
   for i in 0..y.len-1:
     result[i] = y[i]
   result[y.len] = x
 
-proc `&` *[T](x, y: T): seq[T] {.noSideEffect.} =
-  return [x, y]
-
 when not defined(NimrodVM):
   when not defined(ECMAScript):
     proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
diff --git a/lib/system/mm.nim b/lib/system/mm.nim
index 9e207fcf0..76b5d83bd 100755
--- a/lib/system/mm.nim
+++ b/lib/system/mm.nim
@@ -130,17 +130,22 @@ when defined(boehmgc):
 
   include "system/cellsets"
 elif defined(nogc):
-  proc alloc(size: int): pointer =
-    result = c_malloc(size)
-    if result == nil: raiseOutOfMem()
-  proc alloc0(size: int): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
-  proc realloc(p: Pointer, newsize: int): pointer =
-    result = c_realloc(p, newsize)
-    if result == nil: raiseOutOfMem()
-  proc dealloc(p: Pointer) =
-    c_free(p)
+  include "system/alloc"
+
+  when false:
+    proc alloc(size: int): pointer =
+      result = c_malloc(size)
+      if result == nil: raiseOutOfMem()
+    proc alloc0(size: int): pointer =
+      result = alloc(size)
+      zeroMem(result, size)
+    proc realloc(p: Pointer, newsize: int): pointer =
+      result = c_realloc(p, newsize)
+      if result == nil: raiseOutOfMem()
+    proc dealloc(p: Pointer) = c_free(p)
+    proc getOccupiedMem(): int = return -1
+    proc getFreeMem(): int = return -1
+    proc getTotalMem(): int = return -1
 
   proc initGC() = nil
   proc GC_disable() = nil
@@ -151,10 +156,7 @@ elif defined(nogc):
   proc GC_disableMarkAndSweep() = nil
   proc GC_getStatistics(): string = return ""
   
-  proc getOccupiedMem(): int = return -1
-  proc getFreeMem(): int = return -1
-  proc getTotalMem(): int = return -1
-
+  
   proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
     result = alloc0(size)
   proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
@@ -163,7 +165,6 @@ elif defined(nogc):
     cast[PGenericSeq](result).space = len
   proc growObj(old: pointer, newsize: int): pointer =
     result = realloc(old, newsize)
-    # XXX BUG: we need realloc0 here, but C does not support this...
 
   proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
   proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index c0fbe2bb8..40e6e7b11 100755
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -59,6 +59,8 @@ const
   STD_OUTPUT_HANDLE* = -11'i32
   STD_ERROR_HANDLE* = -12'i32
 
+  DETACHED_PROCESS* = 8'i32
+
 proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
     importc: "CloseHandle".}
     
@@ -184,3 +186,7 @@ proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
 proc rdFileTime*(f: FILETIME): int64 = 
   result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
 
+proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
+                                    importc: "Sleep".}
+
+
diff --git a/lib/wrappers/tre/config.h b/lib/wrappers/tre/config.h
index 1a3a0bc3a..1a3a0bc3a 100644..100755
--- a/lib/wrappers/tre/config.h
+++ b/lib/wrappers/tre/config.h
diff --git a/lib/wrappers/tre/tre_all.c b/lib/wrappers/tre/tre_all.c
index 8272657a3..8272657a3 100644..100755
--- a/lib/wrappers/tre/tre_all.c
+++ b/lib/wrappers/tre/tre_all.c
diff --git a/lib/wrappers/tre/version.txt b/lib/wrappers/tre/version.txt
index 4d01880a7..4d01880a7 100644..100755
--- a/lib/wrappers/tre/version.txt
+++ b/lib/wrappers/tre/version.txt
diff --git a/nim/ast.pas b/nim/ast.pas
index cbe5b37fd..8927fc862 100755
--- a/nim/ast.pas
+++ b/nim/ast.pas
@@ -78,27 +78,26 @@ type
     nkBracket, nkBracketExpr, nkPragmaExpr, nkRange, 
     nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkIfExpr, 
     nkElifExpr, nkElseExpr, nkLambda, nkAccQuoted, 
-    nkTableConstr, nkQualified, nkBind, nkSymChoice, 
-    nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkConv, 
-    nkCast, nkAddr, nkHiddenAddr, nkHiddenDeref, 
-    nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64, 
-    nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray, 
-    nkAsgn, nkFastAsgn, nkGenericParams, nkFormalParams, 
-    nkOfInherit, nkModule, nkProcDef, nkMethodDef, 
-    nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef, 
-    nkOfBranch, nkElifBranch, nkExceptBranch, nkElse, 
-    nkMacroStmt, nkAsmStmt, nkPragma, nkIfStmt, 
-    nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, 
-    nkVarSection, nkConstSection, nkConstDef, nkTypeSection, 
-    nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally, 
-    nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt, 
-    nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt, 
-    nkFromStmt, nkIncludeStmt, nkCommentStmt, nkStmtListExpr, 
-    nkBlockExpr, nkStmtListType, nkBlockType, nkTypeOfExpr, 
-    nkObjectTy, nkTupleTy, nkRecList, nkRecCase, 
-    nkRecWhen, nkRefTy, nkPtrTy, nkVarTy, 
-    nkDistinctTy, nkProcTy, nkEnumTy, nkEnumFieldDef, 
-    nkReturnToken);
+    nkTableConstr, nkBind, nkSymChoice, nkHiddenStdConv, 
+    nkHiddenSubConv, nkHiddenCallConv, nkConv, nkCast, 
+    nkAddr, nkHiddenAddr, nkHiddenDeref, nkObjDownConv, 
+    nkObjUpConv, nkChckRangeF, nkChckRange64, nkChckRange, 
+    nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkAsgn, 
+    nkFastAsgn, nkGenericParams, nkFormalParams, nkOfInherit, 
+    nkModule, nkProcDef, nkMethodDef, nkConverterDef, 
+    nkMacroDef, nkTemplateDef, nkIteratorDef, nkOfBranch, 
+    nkElifBranch, nkExceptBranch, nkElse, nkMacroStmt, 
+    nkAsmStmt, nkPragma, nkIfStmt, nkWhenStmt, 
+    nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection, 
+    nkConstSection, nkConstDef, nkTypeSection, nkTypeDef, 
+    nkYieldStmt, nkTryStmt, nkFinally, nkRaiseStmt, 
+    nkReturnStmt, nkBreakStmt, nkContinueStmt, nkBlockStmt, 
+    nkDiscardStmt, nkStmtList, nkImportStmt, nkFromStmt, 
+    nkIncludeStmt, nkCommentStmt, nkStmtListExpr, nkBlockExpr, 
+    nkStmtListType, nkBlockType, nkTypeOfExpr, nkObjectTy, 
+    nkTupleTy, nkRecList, nkRecCase, nkRecWhen, 
+    nkRefTy, nkPtrTy, nkVarTy, nkDistinctTy, 
+    nkProcTy, nkEnumTy, nkEnumFieldDef, nkReturnToken);
   TNodeKinds = set of TNodeKind;
 const
   NodeKindToStr: array [TNodeKind] of string = (
@@ -113,27 +112,26 @@ const
     'nkBracket', 'nkBracketExpr', 'nkPragmaExpr', 'nkRange', 
     'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr', 'nkIfExpr', 
     'nkElifExpr', 'nkElseExpr', 'nkLambda', 'nkAccQuoted', 
-    'nkTableConstr', 'nkQualified', 'nkBind', 'nkSymChoice', 
-    'nkHiddenStdConv', 'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 
-    'nkCast', 'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 
-    'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 
-    'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 
-    'nkAsgn', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 
-    'nkOfInherit', 'nkModule', 'nkProcDef', 'nkMethodDef', 
-    'nkConverterDef', 'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef', 
-    'nkOfBranch', 'nkElifBranch', 'nkExceptBranch', 'nkElse', 
-    'nkMacroStmt', 'nkAsmStmt', 'nkPragma', 'nkIfStmt', 
-    'nkWhenStmt', 'nkForStmt', 'nkWhileStmt', 'nkCaseStmt', 
-    'nkVarSection', 'nkConstSection', 'nkConstDef', 'nkTypeSection', 
-    'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally', 
-    'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 
-    'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 
-    'nkFromStmt', 'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr', 
-    'nkBlockExpr', 'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr', 
-    'nkObjectTy', 'nkTupleTy', 'nkRecList', 'nkRecCase', 
-    'nkRecWhen', 'nkRefTy', 'nkPtrTy', 'nkVarTy', 
-    'nkDistinctTy', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef', 
-    'nkReturnToken');
+    'nkTableConstr', 'nkBind', 'nkSymChoice', 'nkHiddenStdConv', 
+    'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 'nkCast', 
+    'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 'nkObjDownConv', 
+    'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkChckRange', 
+    'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkAsgn', 
+    'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 'nkOfInherit', 
+    'nkModule', 'nkProcDef', 'nkMethodDef', 'nkConverterDef', 
+    'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef', 'nkOfBranch', 
+    'nkElifBranch', 'nkExceptBranch', 'nkElse', 'nkMacroStmt', 
+    'nkAsmStmt', 'nkPragma', 'nkIfStmt', 'nkWhenStmt', 
+    'nkForStmt', 'nkWhileStmt', 'nkCaseStmt', 'nkVarSection', 
+    'nkConstSection', 'nkConstDef', 'nkTypeSection', 'nkTypeDef', 
+    'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkRaiseStmt', 
+    'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkBlockStmt', 
+    'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 'nkFromStmt', 
+    'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 
+    'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr', 'nkObjectTy', 
+    'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen', 
+    'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkDistinctTy', 
+    'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken');
 type
   TSymFlag = (
     sfUsed, sfStar, sfMinus, sfInInterface, 
@@ -141,7 +139,7 @@ type
     sfExportc, sfVolatile, sfRegister, sfPure, 
     sfResult, sfNoSideEffect, sfSideEffect, sfMainModule, 
     sfSystemModule, sfNoReturn, sfAddrTaken, sfCompilerProc, 
-    sfCppMethod, sfDiscriminant, sfDeprecated, sfInClosure, 
+    sfProcvar, sfDiscriminant, sfDeprecated, sfInClosure, 
     sfTypeCheck, sfCompileTime, sfThreadVar, sfMerge, 
     sfDeadCodeElim, sfBorrow);
   TSymFlags = set of TSymFlag;
@@ -152,7 +150,7 @@ const
     'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure', 
     'sfResult', 'sfNoSideEffect', 'sfSideEffect', 'sfMainModule', 
     'sfSystemModule', 'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 
-    'sfCppMethod', 'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 
+    'sfProcvar', 'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 
     'sfTypeCheck', 'sfCompileTime', 'sfThreadVar', 'sfMerge', 
     'sfDeadCodeElim', 'sfBorrow');
 type
@@ -340,6 +338,7 @@ type
     lfNoDeepCopy,  // no need for a deep copy
     lfNoDecl,      // do not declare it in C
     lfDynamicLib,  // link symbol to dynamic library
+    lfExportLib,   // export symbol for dynamic library generation
     lfHeader       // include header file for symbol
   );
 
diff --git a/nim/ccgexprs.pas b/nim/ccgexprs.pas
index 99d36da2d..a5789487a 100755
--- a/nim/ccgexprs.pas
+++ b/nim/ccgexprs.pas
@@ -1086,6 +1086,8 @@ begin
       app(pl, ', ')
   end;
   if (typ.sons[0] <> nil) and invalidRetType then begin
+    // XXX (detected by pegs module 64bit): p(result, result) is not
+    // correct here. Thus we always allocate a temporary:
     if d.k = locNone then getTemp(p, typ.sons[0], d);
     app(pl, addrLoc(d));
   end;
@@ -2212,7 +2214,7 @@ begin
                                 '); unknown symbol')
       end
     end;
-    nkQualified: expr(p, e.sons[1], d);
+    //nkQualified: expr(p, e.sons[1], d);
     nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit,
     nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin
       putIntoDest(p, d, e.typ, genLiteral(p, e));
diff --git a/nim/ccgtypes.pas b/nim/ccgtypes.pas
index db07a6864..1c07fe5c7 100755
--- a/nim/ccgtypes.pas
+++ b/nim/ccgtypes.pas
@@ -221,7 +221,8 @@ begin
   assert(not (sfResult in s.flags));
   case pt.Kind of
     tyObject: begin
-      if (optByRef in s.options) or (getSize(pt) > platform.floatSize) then
+      // XXX quick hack floatSize*2 for the pegs module under 64bit
+      if (optByRef in s.options) or (getSize(pt) > platform.floatSize*2) then
         result := true // requested anyway
       else if (tfFinal in pt.flags) and (pt.sons[0] = nil) then
         result := false // no need, because no subtyping possible
diff --git a/nim/cgen.pas b/nim/cgen.pas
index 17ae8b461..0b74f25b7 100755
--- a/nim/cgen.pas
+++ b/nim/cgen.pas
@@ -659,6 +659,8 @@ var
 begin
   p := newProc(prc, m);
   header := genProcHeader(m, prc);
+  if (gCmd <> cmdCompileToLLVM) and (lfExportLib in prc.loc.flags) then
+    header := con('N_LIB_EXPORT ', header);
   returnStmt := nil;
   assert(prc.ast <> nil);
 
@@ -942,7 +944,11 @@ const
     '  call void @NimMain()$n' +
     '  ret i32 0$n' +
     '}$n';
-  WinNimDllMain = WinNimMain;
+  WinNimDllMain =
+    'N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n' +
+    '  int dummy[8];$n' +{&}
+    CommonMainBody +{&}
+    '}$n';
   WinCDllMain =
     'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
     '                    LPVOID lpvReserved) {$n' +
diff --git a/nim/cgmeth.pas b/nim/cgmeth.pas
index 6b9335c4c..6b9335c4c 100644..100755
--- a/nim/cgmeth.pas
+++ b/nim/cgmeth.pas
diff --git a/nim/commands.pas b/nim/commands.pas
index b281552c9..1663b6b93 100755
--- a/nim/commands.pas
+++ b/nim/commands.pas
@@ -1,7 +1,7 @@
 //

 //

 //           The Nimrod Compiler

-//        (c) Copyright 2008 Andreas Rumpf

+//        (c) Copyright 2009 Andreas Rumpf

 //

 //    See the file "copying.txt", included in this

 //    distribution, for details about the copyright.

@@ -85,7 +85,7 @@ const
 +{&} '  -a, --assertions:on|off   code generation for assertions ON|OFF' +{&} nl
 +{&} '  --dead_code_elim:on|off   whole program dead code elimination ON|OFF' +{&} nl
 +{&} '  --opt:none|speed|size     optimize not at all or for speed|size' +{&} nl
-+{&} '  --app:console|gui         generate a console|GUI application' +{&} nl
++{&} '  --app:console|gui|lib     generate a console|GUI application|dynamic library' +{&} nl
 +{&} '  -r, --run                 run the compiled program with given arguments' +{&} nl
 +{&} '  --advanced                show advanced command line switches' +{&} nl
 +{&} '  -h, --help                show this help' +{&} nl
@@ -127,10 +127,12 @@ const
 +{&} '  --checkpoints:on|off      turn on|off checkpoints; for debugging Nimrod' +{&} nl
 +{&} '  --skip_cfg                do not read the general configuration file' +{&} nl
 +{&} '  --skip_proj_cfg           do not read the project''s configuration file' +{&} nl
-+{&} '  --gc:refc|boehm           use Nimrod''s native GC|Boehm GC' +{&} nl
++{&} '  --gc:refc|boehm|none      use Nimrod''s native GC|Boehm GC|no GC' +{&} nl
 +{&} '  --index:FILE              use FILE to generate a documenation index file' +{&} nl
 +{&} '  --putenv:key=value        set an environment variable' +{&} nl
 +{&} '  --list_cmd                list the commands used to execute external programs' +{&} nl
++{&} '  --parallel_build=0|1|...  perform a parallel build' +{&} nl
++{&} '                            value = number of processors (0 for auto-detect)' +{&} nl
 +{&} '  --verbosity:0|1|2|3       set Nimrod''s verbosity level (0 is default)' +{&} nl
 +{&} '  -v, --version             show detailed version information' +{&} nl
 //[[[end]]]

@@ -152,7 +154,7 @@ begin
   if (pass = passCmd1) and not helpWritten then begin

     // BUGFIX 19

     MessageOut(getCommandLineDesc());

-    helpWritten := true;
+    helpWritten := true;

     halt(0);

   end

 end;

@@ -362,10 +364,10 @@ begin
     wNoLinking: begin

       expectNoArg(switch, arg, pass, info);

       include(gGlobalOptions, optNoLinking);

-    end;
-    wNoMain: begin
+    end;

+    wNoMain: begin

       expectNoArg(switch, arg, pass, info);

-      include(gGlobalOptions, optNoMain);    
+      include(gGlobalOptions, optNoMain);    

     end;

     wForceBuild, wF: begin

       expectNoArg(switch, arg, pass, info);

@@ -523,6 +525,10 @@ begin
       expectArg(switch, arg, pass, info);

       gVerbosity := parseInt(arg);

     end;

+    wParallelBuild: begin

+      expectArg(switch, arg, pass, info);      

+      gNumberOfProcessors := parseInt(arg);

+    end;

     wVersion, wV: begin

       expectNoArg(switch, arg, pass, info);

       writeVersionInfo(pass);

diff --git a/nim/depends.pas b/nim/depends.pas
index d8b978142..c5b844044 100755
--- a/nim/depends.pas
+++ b/nim/depends.pas
@@ -51,12 +51,12 @@ begin
   case n.kind of
     nkImportStmt: begin
       for i := 0 to sonsLen(n)-1 do begin
-        imported := getFileTrunk(getModuleFile(n.sons[i]));
+        imported := extractFileTrunk(getModuleFile(n.sons[i]));
         addDependencyAux(g.module.name.s, imported);
       end
     end;
     nkFromStmt: begin
-      imported := getFileTrunk(getModuleFile(n.sons[0]));
+      imported := extractFileTrunk(getModuleFile(n.sons[0]));
       addDependencyAux(g.module.name.s, imported);
     end;
     nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: begin
diff --git a/nim/docgen.pas b/nim/docgen.pas
index 4d94a2a68..468dd1bc9 100755
--- a/nim/docgen.pas
+++ b/nim/docgen.pas
@@ -19,7 +19,7 @@ interface
 
 uses
   nsystem, charsets, ast, astalgo, strutils, nhashes, options, nversion, msgs,
-  nos, ropes, idents, wordrecg, nmath, pnimsyn, rnimsyn, scanner, rst, ntime,
+  nos, ropes, idents, wordrecg, nmath, syntaxes, rnimsyn, scanner, rst, ntime,
   highlite;
 
 procedure CommandDoc(const filename: string);
@@ -85,7 +85,7 @@ var
   dummyHasToc: bool;
 begin
   if gIndexFile = '' then exit;
-  gIndexFile := appendFileExt(gIndexFile, 'txt');
+  gIndexFile := addFileExt(gIndexFile, 'txt');
   d.indexValFilename := changeFileExt(extractFilename(d.filename), HtmlExt);
   if ExistsFile(gIndexFile) then begin
     d.indexFile := rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1,
@@ -1135,7 +1135,7 @@ var
   ast: PNode;
   d: PDoc;
 begin
-  ast := parseFile(appendFileExt(filename, nimExt));
+  ast := parseFile(addFileExt(filename, nimExt));
   if ast = nil then exit;
   d := newDocumentor(filename);
   initIndexFile(d);
@@ -1152,7 +1152,7 @@ var
   rst: PRstNode;
   code: PRope;
 begin
-  filen := appendFileExt(filename, 'txt');
+  filen := addFileExt(filename, 'txt');
   d := newDocumentor(filen);
   initIndexFile(d);
   rst := rstParse(readFile(filen), false, filen, 0, 1, d.hasToc);
diff --git a/nim/ecmasgen.pas b/nim/ecmasgen.pas
index 8edd2605a..59cb3c330 100755
--- a/nim/ecmasgen.pas
+++ b/nim/ecmasgen.pas
@@ -1096,7 +1096,7 @@ begin
       end;
     end;
     nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r);
-    nkDotExpr, nkQualified: genFieldAddr(p, n, r);
+    nkDotExpr: genFieldAddr(p, n, r);
     nkBracketExpr: genArrayAddr(p, n, r);
     else InternalError(n.info, 'genAddr');
   end
diff --git a/nim/evals.pas b/nim/evals.pas
index 3787ff371..bb6c8a2b1 100755
--- a/nim/evals.pas
+++ b/nim/evals.pas
@@ -1324,7 +1324,7 @@ begin
     nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
     nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
     nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
-    nkQualified, nkLambda, nkContinueStmt, nkIdent:
+    nkLambda, nkContinueStmt, nkIdent:
       stackTrace(c, n, errCannotInterpretNodeX, nodeKindToStr[n.kind]);
     else InternalError(n.info, 'evalAux: ' + nodekindToStr[n.kind]);
   end;
diff --git a/nim/extccomp.pas b/nim/extccomp.pas
index 9e7c7e4cc..aab4febb7 100755
--- a/nim/extccomp.pas
+++ b/nim/extccomp.pas
@@ -1,7 +1,7 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
@@ -304,10 +304,10 @@ procedure setCC(const ccname: string);
 var
   i: TSystemCC;
 begin
-  linkOptions := '';
   ccompiler := nameToCC(ccname);
   if ccompiler = ccNone then rawMessage(errUnknownCcompiler, ccname);
   compileOptions := getConfigVar(CC[ccompiler].name + '.options.always');
+  linkOptions := getConfigVar(CC[ccompiler].name + '.options.linker');
   ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
   for i := low(CC) to high(CC) do undefSymbol(CC[i].name);
   defineSymbol(CC[ccompiler].name);
@@ -323,6 +323,7 @@ begin
   if gCmd = cmdCompileToCpp then
     cExt := '.cpp';
   addCompileOption(getConfigVar(CC[ccompiler].name + '.options.always'));
+  addLinkOption(getConfigVar(CC[ccompiler].name + '.options.linker'));
   if length(ccompilerPath) = 0 then
     ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
 end;
@@ -387,8 +388,8 @@ end;
 procedure execExternalProgram(const cmd: string);
 begin
   if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
-    MessageOut('Executing: ' +{&} nl +{&} cmd);
-  if executeCommand(cmd) <> 0 then
+    MessageOut(cmd);
+  if execCmd(cmd) <> 0 then
     rawMessage(errExecutionOfProgramFailed);
 end;
 
@@ -398,7 +399,7 @@ var
 begin
   splitPath(projectFile, path, scriptname);
   SplitFilename(scriptname, name, ext);
-  name := appendFileExt('compile_' + name, platform.os[targetOS].scriptExt);
+  name := addFileExt('compile_' + name, platform.os[targetOS].scriptExt);
   WriteRope(script, joinPath(path, name));
 end;
 
@@ -437,7 +438,7 @@ var
 begin
   c := ccompiler;
   options := compileOptions;
-  trunk := getFileTrunk(cfilename);
+  trunk := extractFileTrunk(cfilename);
   if optCDebug in gGlobalOptions then begin
     key := trunk + '.debug';
     if existsConfigVar(key) then
@@ -468,7 +469,7 @@ begin
   key := cc[c].name + '.exe';
   if existsConfigVar(key) then
     exe := getConfigVar(key);
-  if targetOS = osWindows then exe := appendFileExt(exe, 'exe');
+  if targetOS = osWindows then exe := addFileExt(exe, 'exe');
 
   if (optGenDynLib in gGlobalOptions)
   and (ospNeedsPIC in platform.OS[targetOS].props) then
@@ -494,7 +495,7 @@ begin
     objfile := toObjFile(cfile)
   else
     objfile := completeCFilePath(toObjFile(cfile));
-  cfile := quoteIfContainsWhite(AppendFileExt(cfile, cExt));
+  cfile := quoteIfContainsWhite(AddFileExt(cfile, cExt));
   objfile := quoteIfContainsWhite(objfile);
   
   result := quoteIfContainsWhite(format(compilePattern,
@@ -517,7 +518,9 @@ begin
 end;
 
 procedure CompileCFile(const list: TLinkedList;
-                       var script: PRope; isExternal: Boolean);
+                       var script: PRope; 
+                       var cmds: TStringSeq;
+                       isExternal: Boolean);
 var
   it: PStrEntry;
   compileCmd: string;
@@ -528,7 +531,7 @@ begin
     // call the C compiler for the .c file:
     compileCmd := getCompileCFileCmd(it.data, isExternal);
     if not (optCompileOnly in gGlobalOptions) then
-      execExternalProgram(compileCmd);
+      add(cmds, compileCmd); //execExternalProgram(compileCmd);
     if (optGenScript in gGlobalOptions) then begin
       app(script, compileCmd);
       app(script, tnl);
@@ -543,6 +546,8 @@ var
   linkCmd, objfiles, exefile, buildgui, builddll, linkerExe: string;
   c: TSystemCC; // an alias to ccompiler
   script: PRope;
+  cmds: TStringSeq;
+  res, i: int;
 begin
   if (gGlobalOptions * [optCompileOnly, optGenScript] = [optCompileOnly]) then
     exit; // speed up that call if only compiling and no script shall be
@@ -551,41 +556,53 @@ begin
   fileCounter := 0;
   c := ccompiler;
   script := nil;
-  CompileCFile(toCompile, script, false);
-  CompileCFile(externalToCompile, script, true);
-
+  cmds := {@ignore} nil {@emit @[]};
+  CompileCFile(toCompile, script, cmds, false);
+  CompileCFile(externalToCompile, script, cmds, true);
+  if not (optCompileOnly in gGlobalOptions) then begin
+    if gNumberOfProcessors = 0 then 
+      gNumberOfProcessors := countProcessors();
+    if gNumberOfProcessors <= 1 then begin
+      res := 0;
+      for i := 0 to high(cmds) do res := max(execCmd(cmds[i]), res);
+    end
+    else if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
+      res := execProcesses(cmds, {@set}[poEchoCmd, poUseShell, poParentStreams], 
+                           gNumberOfProcessors)
+    else
+      res := execProcesses(cmds, {@set}[poUseShell, poParentStreams],
+                           gNumberOfProcessors);
+    if res <> 0 then
+      rawMessage(errExecutionOfProgramFailed);
+  end;
+  
   if not (optNoLinking in gGlobalOptions) then begin
     // call the linker:
     linkerExe := getConfigVar(cc[c].name + '.linkerexe');
     if length(linkerExe) = 0 then linkerExe := cc[c].linkerExe;
-    if targetOS = osWindows then linkerExe := appendFileExt(linkerExe, 'exe');
+    if targetOS = osWindows then linkerExe := addFileExt(linkerExe, 'exe');
 
     if (platform.hostOS <> targetOS) then
       linkCmd := quoteIfContainsWhite(linkerExe)
     else
       linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe));
 
-    if optGenDynLib in gGlobalOptions then
-      buildDll := cc[c].buildDll
-    else
-      buildDll := '';
     if optGenGuiApp in gGlobalOptions then
       buildGui := cc[c].buildGui
     else
       buildGui := '';
-
-    if optGenDynLib in gGlobalOptions then
-      exefile := platform.os[targetOS].dllPrefix
-    else
-      exefile := '';
+      
+    if optGenDynLib in gGlobalOptions then begin
+      exefile := format(platform.os[targetOS].dllFrmt,
+                        [extractFileTrunk(projectFile)]);
+      buildDll := cc[c].buildDll;
+    end
+    else begin
+      exefile := extractFileTrunk(projectFile) +{&} platform.os[targetOS].exeExt;
+      buildDll := '';
+    end;
     if targetOS = platform.hostOS then
-      add(exefile, projectFile)
-    else
-      add(exefile, extractFileName(projectFile));
-    if optGenDynLib in gGlobalOptions then
-      add(exefile, platform.os[targetOS].dllExt)
-    else
-      add(exefile, platform.os[targetOS].exeExt);
+      exefile := joinPath(extractDir(projectFile), exefile);
     exefile := quoteIfContainsWhite(exefile);
 
     it := PStrEntry(toLink.head);
@@ -639,7 +656,7 @@ begin
   result := nil;
   it := PStrEntry(list.head);
   while it <> nil do begin
-    appf(result, '--file:r"$1"$n', [toRope(AppendFileExt(it.data, cExt))]);
+    appf(result, '--file:r"$1"$n', [toRope(AddFileExt(it.data, cExt))]);
     it := PStrEntry(it.next);
   end;
 end;
diff --git a/nim/filters.pas b/nim/filters.pas
new file mode 100755
index 000000000..95f628fe2
--- /dev/null
+++ b/nim/filters.pas
@@ -0,0 +1,137 @@
+//
+//
+//           The Nimrod Compiler
+//        (c) Copyright 2009 Andreas Rumpf
+//
+//    See the file "copying.txt", included in this
+//    distribution, for details about the copyright.
+//
+unit filters;
+
+// This module implements Nimrod's simple filters and helpers for filters.
+
+{$include config.inc}
+
+interface
+
+uses
+  nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
+  ast, astalgo, msgs, options, rnimsyn;
+
+function filterReplace(input: PLLStream; const filename: string;
+                       call: PNode): PLLStream;
+function filterStrip(input: PLLStream; const filename: string;
+                     call: PNode): PLLStream;
+
+// helpers to retrieve arguments:
+function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
+function strArg(n: PNode; const name: string; pos: int;
+                const default: string): string;
+function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
+
+implementation
+
+procedure invalidPragma(n: PNode);
+begin
+  liMessage(n.info, errXNotAllowedHere, renderTree(n, {@set}[renderNoComments]));
+end;
+
+function getArg(n: PNode; const name: string; pos: int): PNode;
+var
+  i: int;
+begin
+  result := nil;
+  if n.kind in [nkEmpty..nkNilLit] then exit;
+  for i := 1 to sonsLen(n)-1 do
+    if n.sons[i].kind = nkExprEqExpr then begin
+      if n.sons[i].sons[0].kind <> nkIdent then invalidPragma(n);
+      if IdentEq(n.sons[i].sons[0].ident, name) then begin
+        result := n.sons[i].sons[1];
+        exit
+      end
+    end
+    else if i = pos then begin
+      result := n.sons[i]; exit
+    end
+end;
+
+function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
+var
+  x: PNode;
+begin
+  x := getArg(n, name, pos);
+  if x = nil then result := default
+  else if x.kind = nkCharLit then result := chr(int(x.intVal))
+  else invalidPragma(n);
+end;
+
+function strArg(n: PNode; const name: string; pos: int;
+                const default: string): string;
+var
+  x: PNode;
+begin
+  x := getArg(n, name, pos);
+  if x = nil then result := default
+  else if x.kind in [nkStrLit..nkTripleStrLit] then result := x.strVal
+  else invalidPragma(n);
+end;
+
+function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
+var
+  x: PNode;
+begin
+  x := getArg(n, name, pos);
+  if x = nil then result := default
+  else if (x.kind = nkIdent) and IdentEq(x.ident, 'true') then result := true
+  else if (x.kind = nkIdent) and IdentEq(x.ident, 'false') then result := false
+  else invalidPragma(n);
+end;
+
+// -------------------------- strip filter -----------------------------------
+
+function filterStrip(input: PLLStream; const filename: string;
+                     call: PNode): PLLStream;
+var
+  line, pattern, stripped: string;
+  leading, trailing: bool;
+begin
+  pattern := strArg(call, 'startswith', 1, '');
+  leading := boolArg(call, 'leading', 2, true);
+  trailing := boolArg(call, 'trailing', 3, true);
+  
+  result := LLStreamOpen('');
+  while not LLStreamAtEnd(input) do begin
+    line := LLStreamReadLine(input);
+  {@ignore}
+    stripped := strip(line);
+  {@emit
+    stripped := strip(line, leading, trailing);
+  }
+    if (length(pattern) = 0) or startsWith(stripped, pattern) then 
+      LLStreamWriteln(result, stripped)
+    else
+      LLStreamWriteln(result, line)
+  end;
+  LLStreamClose(input);
+end;
+
+// -------------------------- replace filter ---------------------------------
+
+function filterReplace(input: PLLStream; const filename: string;
+                       call: PNode): PLLStream;
+var
+  line, sub, by: string;
+begin
+  sub := strArg(call, 'sub', 1, '');
+  if length(sub) = 0 then invalidPragma(call);
+  by := strArg(call, 'by', 2, '');
+
+  result := LLStreamOpen('');  
+  while not LLStreamAtEnd(input) do begin
+    line := LLStreamReadLine(input);
+    LLStreamWriteln(result, replace(line, sub, by))
+  end;
+  LLStreamClose(input);    
+end;
+
+end.
diff --git a/nim/importer.pas b/nim/importer.pas
index d031ffe3a..a1ed57978 100755
--- a/nim/importer.pas
+++ b/nim/importer.pas
@@ -29,9 +29,8 @@ implementation
 function findModule(const info: TLineInfo; const modulename: string): string;
 // returns path to module
 begin
-  result := options.FindFile(AppendFileExt(modulename, nimExt));
-  if result = '' then
-    liMessage(info, errCannotOpenFile, modulename);
+  result := options.FindFile(AddFileExt(modulename, nimExt));
+  if result = '' then liMessage(info, errCannotOpenFile, modulename);
 end;
 
 function getModuleFile(n: PNode): string;
diff --git a/nim/llstream.pas b/nim/llstream.pas
index df4c823a6..30d9c0287 100755
--- a/nim/llstream.pas
+++ b/nim/llstream.pas
@@ -1,7 +1,7 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
@@ -25,7 +25,7 @@ type
     kind: TLLStreamKind; // accessible for low-level access (lexbase uses this)
     f: TBinaryFile;
     s: string;
-    pos: int; // for string streams
+    rd, wr: int; // for string streams
   end;
   PLLStream = ^TLLStream;
   
@@ -46,6 +46,8 @@ procedure LLStreamWrite(s: PLLStream; const data: string); overload;
 procedure LLStreamWrite(s: PLLStream; data: Char); overload;
 procedure LLStreamWrite(s: PLLStream; buf: pointer; buflen: int); overload;
 
+procedure LLStreamWriteln(s: PLLStream; const data: string);
+
 function LLStreamAtEnd(s: PLLStream): bool;
 
 implementation
@@ -97,7 +99,6 @@ begin
   {@emit}
   result.kind := llsStdIn;
   result.s := '';
-  result.pos := -1;
 end;
 
 procedure LLStreamClose(s: PLLStream);
@@ -114,7 +115,7 @@ var
   L: int;
 begin
   s.s := '';
-  s.pos := 0;
+  s.rd := 0;
   while true do begin
     write(output, 'Nimrod> ');
     line := readLine(input);
@@ -123,10 +124,10 @@ begin
     add(s.s, nl);
     if (L > 0) and (line[L-1+strStart] = '#') then break;
   end;
-  result := min(bufLen, length(s.s)-s.pos);
+  result := min(bufLen, length(s.s)-s.rd);
   if result > 0 then begin
-    copyMem(buf, addr(s.s[strStart+s.pos]), result);
-    inc(s.pos, result)
+    copyMem(buf, addr(s.s[strStart+s.rd]), result);
+    inc(s.rd, result)
   end
 end;
 
@@ -135,10 +136,10 @@ begin
   case s.kind of 
     llsNone: result := 0;
     llsString: begin
-      result := min(bufLen, length(s.s)-s.pos);
+      result := min(bufLen, length(s.s)-s.rd);
       if result > 0 then begin
-        copyMem(buf, addr(s.s[strStart+s.pos]), result);
-        inc(s.pos, result)
+        copyMem(buf, addr(s.s[strStart+s.rd]), result);
+        inc(s.rd, result)
       end
     end;
     llsFile:  result := readBuffer(s.f, buf, bufLen);
@@ -152,20 +153,20 @@ begin
     llsNone: result := '';
     llsString: begin
       result := '';
-      while s.pos < length(s.s) do begin
-        case s.s[s.pos+strStart] of 
+      while s.rd < length(s.s) do begin
+        case s.s[s.rd+strStart] of 
           #13: begin 
-            inc(s.pos); 
-            if s.s[s.pos+strStart] = #10 then inc(s.pos);
+            inc(s.rd); 
+            if s.s[s.rd+strStart] = #10 then inc(s.rd);
             break
           end;
-          #10: begin inc(s.pos); break end;
+          #10: begin inc(s.rd); break end;
           else begin
-            addChar(result, s.s[s.pos+strStart]);
-            inc(s.pos);
+            addChar(result, s.s[s.rd+strStart]);
+            inc(s.rd);
           end
         end
-      end    
+      end
     end;
     llsFile: result := readLine(s.f);
     llsStdIn: result := readLine(input);
@@ -176,7 +177,7 @@ function LLStreamAtEnd(s: PLLStream): bool;
 begin
   case s.kind of
     llsNone: result := true;
-    llsString: result := s.pos < length(s.s);
+    llsString: result := s.rd >= length(s.s);
     llsFile: result := endOfFile(s.f);
     llsStdIn: result := false;
   end
@@ -186,9 +187,15 @@ procedure LLStreamWrite(s: PLLStream; const data: string); overload;
 begin
   case s.kind of 
     llsNone, llsStdIn: begin end;
-    llsString: add(s.s, data);
+    llsString: begin add(s.s, data); inc(s.wr, length(data)) end;
     llsFile: nimWrite(s.f, data);
-  end
+  end;
+end;
+
+procedure LLStreamWriteln(s: PLLStream; const data: string);
+begin
+  LLStreamWrite(s, data);
+  LLStreamWrite(s, nl);
 end;
 
 procedure LLStreamWrite(s: PLLStream; data: Char); overload;
@@ -197,7 +204,7 @@ var
 begin
   case s.kind of 
     llsNone, llsStdIn: begin end;
-    llsString: addChar(s.s, data);
+    llsString: begin addChar(s.s, data); inc(s.wr); end;
     llsFile: begin
       c := data;
       {@discard} writeBuffer(s.f, addr(c), sizeof(c));
@@ -212,8 +219,8 @@ begin
     llsString: begin
       if bufLen > 0 then begin
         setLength(s.s, length(s.s) + bufLen);
-        copyMem(addr(s.s[strStart+s.pos]), buf, bufLen);
-        inc(s.pos, bufLen);
+        copyMem(addr(s.s[strStart+s.wr]), buf, bufLen);
+        inc(s.wr, bufLen);
       end
     end;
     llsFile: {@discard} writeBuffer(s.f, buf, bufLen);
@@ -229,9 +236,9 @@ begin
   case s.kind of 
     llsNone, llsStdIn: result := '';
     llsString: begin
-      if s.pos = 0 then result := s.s
-      else result := ncopy(s.s, s.pos+strStart);
-      s.pos := length(s.s);
+      if s.rd = 0 then result := s.s
+      else result := ncopy(s.s, s.rd+strStart);
+      s.rd := length(s.s);
     end;
     llsFile: begin
       result := newString(bufSize);
diff --git a/nim/lookups.pas b/nim/lookups.pas
index d68095635..e4c07224f 100755
--- a/nim/lookups.pas
+++ b/nim/lookups.pas
@@ -177,7 +177,7 @@ begin
       if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id) then
         liMessage(n.info, errUseQualifier, n.sym.name.s)
     end;
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       result := nil;
       m := qualifiedLookUp(c, n.sons[0], false);
       if (m <> nil) and (m.kind = skModule) then begin
@@ -237,7 +237,7 @@ begin
         result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name);
       end; *)
     end; 
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       o.mode := oimOtherModule;
       o.m := qualifiedLookUp(c, n.sons[0], false);
       if (o.m <> nil) and (o.m.kind = skModule) then begin
diff --git a/nim/main.pas b/nim/main.pas
index 1bb2ce586..c3f2cbd85 100755
--- a/nim/main.pas
+++ b/nim/main.pas
@@ -15,10 +15,10 @@ unit main;
 interface
 
 uses
-  nsystem, llstream, strutils, ast, astalgo, scanner, pnimsyn, rnimsyn, 
+  nsystem, llstream, strutils, ast, astalgo, scanner, syntaxes, rnimsyn, 
   options, msgs, nos, lists, condsyms, paslex, pasparse, rodread, rodwrite,
   ropes, trees, wordrecg, sem, semdata, idents, passes, docgen,
-  extccomp, cgen, ecmasgen, platform, ptmplsyn, interact, nimconf, importer,
+  extccomp, cgen, ecmasgen, platform, interact, nimconf, importer,
   passaux, depends, transf, evals, types;
 
 procedure MainCommand(const cmd, filename: string);
@@ -69,7 +69,7 @@ begin
 {@emit}
   result.id := -1; // for better error checking
   result.kind := skModule;
-  result.name := getIdent(getFileTrunk(filename));
+  result.name := getIdent(extractFileTrunk(filename));
   result.owner := result; // a module belongs to itself
   result.info := newLineInfo(filename, 1, 1);
   include(result.flags, sfUsed);
@@ -101,7 +101,7 @@ var
   f: string;
 begin
   rd := nil;
-  f := appendFileExt(filename, nimExt);
+  f := addFileExt(filename, nimExt);
   result := newModule(filename);
   if isMainFile then include(result.flags, sfMainModule);
   if isSystemFile then include(result.flags, sfSystemModule);
@@ -118,8 +118,8 @@ end;
 procedure CompileProject(const filename: string);
 begin
   {@discard} CompileModule(
-    JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
-  {@discard} CompileModule(appendFileExt(filename, nimExt), true, false);
+    JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
+  {@discard} CompileModule(addFileExt(filename, nimExt), true, false);
 end;
 
 procedure semanticPasses;
@@ -137,7 +137,7 @@ begin
   compileProject(filename);
   generateDot(filename);
   execExternalProgram('dot -Tpng -o' +{&} changeFileExt(filename, 'png') +{&}
-                      ' ' +{&} changeFileExt(filename, 'dot'));
+                                 ' ' +{&} changeFileExt(filename, 'dot'));
 end;
 
 procedure CommandCheck(const filename: string);
@@ -185,7 +185,7 @@ begin
   
   // load system module:
   {@discard} CompileModule(
-    JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
+    JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
 
   m := newModule('stdin');
   m.id := getID();
@@ -228,7 +228,7 @@ procedure CommandExportSymbols(const filename: string);
 var
   module: PNode;
 begin
-  module := parseFile(appendFileExt(filename, NimExt));
+  module := parseFile(addFileExt(filename, NimExt));
   if module <> nil then begin
     exSymbols(module);
     renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
@@ -239,7 +239,7 @@ procedure CommandPretty(const filename: string);
 var
   module: PNode;
 begin
-  module := parseFile(appendFileExt(filename, NimExt));
+  module := parseFile(addFileExt(filename, NimExt));
   if module <> nil then
     renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
 end;
@@ -255,7 +255,7 @@ begin
   fillChar(tok, sizeof(tok), 0);
   fillChar(L, sizeof(L), 0);
 {@emit}
-  f := appendFileExt(filename, 'pas');
+  f := addFileExt(filename, 'pas');
   stream := LLStreamOpen(f, fmRead);
   if stream <> nil then begin
     OpenLexer(L, f, stream);
@@ -277,7 +277,7 @@ var
   f: string;
   stream: PLLStream;
 begin
-  f := appendFileExt(filename, 'pas');
+  f := addFileExt(filename, 'pas');
   stream := LLStreamOpen(f, fmRead);
   if stream <> nil then begin
     OpenPasParser(p, f, stream);
@@ -300,7 +300,7 @@ begin
 {@ignore}
   fillChar(tok^, sizeof(tok^), 0);
 {@emit}
-  f := appendFileExt(filename, nimExt);
+  f := addFileExt(filename, nimExt);
   stream := LLStreamOpen(f, fmRead);
   if stream <> nil then begin 
     openLexer(L, f, stream);
@@ -331,8 +331,7 @@ begin
     prependStr(searchPaths, dir);
   end;
   setID(100);
-  passes.gIncludeFile := parseFile;
-  passes.gIncludeTmplFile := ptmplsyn.parseTmplFile;
+  passes.gIncludeFile := syntaxes.parseFile;
   passes.gImportModule := importModule;
 
   case whichKeyword(cmd) of
@@ -408,7 +407,7 @@ begin
     wParse: begin
       gCmd := cmdParse;
       wantFile(filename);
-      {@discard} parseFile(appendFileExt(filename, nimExt));
+      {@discard} parseFile(addFileExt(filename, nimExt));
     end;
     wScan: begin
       gCmd := cmdScan;
diff --git a/nim/msgs.pas b/nim/msgs.pas
index 9d421a5bf..f1a3a200a 100755
--- a/nim/msgs.pas
+++ b/nim/msgs.pas
@@ -127,7 +127,7 @@ type
     errOverOrUnderflow,
     errCannotEvalXBecauseIncompletelyDefined,
     errChrExpectsRange0_255,
-    errDotRequiresRecordOrObjectType,
+    errDynlibRequiresExportc,
     errUndeclaredFieldX,
     errNilAccess,
     errIndexOutOfBounds,
@@ -182,7 +182,7 @@ type
     errButExpectedX,
     errAmbiguousCallXYZ,
     errWrongNumberOfArguments,
-    errInlineProcHasNoAddress,
+    errXCannotBePassedToProcVar,
     errXCannotBeInParamDecl,
     errPragmaOnlyInHeaderOfProc,
     errImplOfXNotAllowed,
@@ -215,7 +215,6 @@ type
     errATypeHasNoValue,
     errXisNoType,
     errCircumNeedsPointer,
-    errInvalidContextForBuiltinX,
     errInvalidExpression,
     errInvalidExpressionX,
     errEnumHasNoValueX,
@@ -272,6 +271,7 @@ type
     warnUnknownSubstitutionX,
     warnLanguageXNotSupported,
     warnCommentXIgnored,
+    warnXisPassedToProcVar,
     warnUser,
     hintSuccess,
     hintSuccessX,
@@ -365,7 +365,7 @@ const
     'over- or underflow',
     'cannot evalutate ''$1'' because type is not defined completely',
     '''chr'' expects an int in the range 0..255',
-    '''.'' requires a record or object type',
+    '''dynlib'' requires ''exportc''',
     'undeclared field: ''$1''',
     'attempt to access a nil address',
     'index out of bounds',
@@ -420,7 +420,7 @@ const
     'but expected ''$1''',
     'ambiguous call; both $1 and $2 match for: $3',
     'wrong number of arguments',
-    'an inline proc has no address',
+    '''$1'' cannot be passed to a procvar',
     '$1 cannot be declared in parameter declaration',
     'pragmas are only in the header of a proc allowed',
     'implementation of ''$1'' is not allowed',
@@ -453,7 +453,6 @@ const
     'a type has no value',
     'invalid type: ''$1''',
     '''^'' needs a pointer or reference type',
-    'invalid context for builtin ''$1''',
     'invalid expression',
     'invalid expression: ''$1''',
     'enum has no value ''$1''',
@@ -510,6 +509,7 @@ const
     'unknown substitution ''$1'' [UnknownSubstitutionX]',
     'language ''$1'' not supported [LanguageXNotSupported]',
     'comment ''$1'' ignored [CommentXIgnored]',
+    '''$1'' is passed to a procvar; deprecated [XisPassedToProcVar]',
     '$1 [User]',
     'operation successful [Success]',
     'operation successful ($1 lines compiled; $2 sec total) [SuccessX]',
@@ -526,7 +526,7 @@ const
     '$1 [User]'
   );
 const
-  WarningsToStr: array [0..13] of string = (
+  WarningsToStr: array [0..14] of string = (
     'CannotOpenFile',
     'OctalEscape',
     'XIsNeverRead',
@@ -540,6 +540,7 @@ const
     'UnknownSubstitutionX',
     'LanguageXNotSupported',
     'CommentXIgnored',
+    'XisPassedToProcVar',
     'User'
   );
 const
diff --git a/nim/nimrod.pas b/nim/nimrod.pas
index 728325ccc..b3247bff0 100755
--- a/nim/nimrod.pas
+++ b/nim/nimrod.pas
@@ -1,7 +1,7 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2006 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
@@ -28,15 +28,27 @@ var
 procedure ProcessCmdLine(pass: TCmdLinePass; var command, filename: string);
 var
   p: TOptParser;
+  bracketLe: int;
+  key, val: string;
 begin
   p := parseopt.init();
   while true do begin
     parseopt.next(p);
     case p.kind of
       cmdEnd: break;
-      cmdLongOption, cmdShortOption:
-        ProcessSwitch(p.key, p.val, pass, cmdLineInfo);
-      cmdArgument: begin
+      cmdLongOption, cmdShortOption: begin
+        // hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
+        // we fix this here
+        bracketLe := strutils.find(p.key, '[');
+        if bracketLe >= strStart then begin
+          key := ncopy(p.key, strStart, bracketLe-1);
+          val := ncopy(p.key, bracketLe+1) +{&} ':' +{&} p.val;
+          ProcessSwitch(key, val, pass, cmdLineInfo);
+        end
+        else
+          ProcessSwitch(p.key, p.val, pass, cmdLineInfo);
+      end;
+      cmdArgument: begin  
         if command = '' then command := p.key
         else if filename = '' then begin
           filename := unixToNativePath(p.key);
diff --git a/nim/nos.pas b/nim/nos.pas
index 2edb0864e..7b9d2ee09 100755
--- a/nim/nos.pas
+++ b/nim/nos.pas
@@ -74,7 +74,7 @@ function GetConfigDir(): string;
 procedure SplitFilename(const filename: string; out name, extension: string);
 
 function ExistsFile(const filename: string): Boolean;
-function AppendFileExt(const filename, ext: string): string;
+function AddFileExt(const filename, ext: string): string;
 function ChangeFileExt(const filename, ext: string): string;
 
 procedure createDir(const dir: string);
@@ -84,8 +84,20 @@ function UnixToNativePath(const path: string): string;
 
 function sameFile(const path1, path2: string): boolean;
 
+
+function extractFileTrunk(const filename: string): string;
+
+
 implementation
 
+function extractFileTrunk(const filename: string): string;
+var
+  f, e, dir: string;
+begin
+  splitPath(filename, dir, f);
+  splitFilename(f, result, e);
+end;
+
 function GetConfigDir(): string;
 begin
 {$ifdef windows}
@@ -103,7 +115,7 @@ end;
 function UnixToNativePath(const path: string): string;
 begin
   if dirSep <> '/' then
-    result := replaceStr(path, '/', dirSep)
+    result := replace(path, '/', dirSep)
   else
     result := path;
 end;
@@ -150,7 +162,7 @@ begin
     result := extSep + ext
 end;
 
-function AppendFileExt(const filename, ext: string): string;
+function AddFileExt(const filename, ext: string): string;
 var
   extPos: int;
 begin
diff --git a/nim/nsystem.pas b/nim/nsystem.pas
index 49d17b172..4cdfade93 100755
--- a/nim/nsystem.pas
+++ b/nim/nsystem.pas
@@ -56,6 +56,11 @@ const
   snil = '';
 
 type
+  TStringSeq = array of string;
+  TCharSet = set of Char;
+
+
+type
   Natural = 0..high(int);
   Positive = 1..high(int);
   NObject = object // base type for all objects, cannot use
@@ -214,9 +219,11 @@ function readFile(const filename: string): string;
 
 procedure nimWrite(var f: tBinaryFile; const str: string); overload;
 
-procedure add(var x: string; const y: string);
+procedure add(var x: string; const y: string); overload;
 // Pascal version of string appending. Terminating zero is ignored.
 
+procedure add(var s: TStringSeq; const y: string); overload;
+
 function isNil(s: string): bool;
 
 implementation
@@ -238,6 +245,15 @@ begin
     else x := x + y;
   end
 end;
+
+procedure add(var s: TStringSeq; const y: string); overload;
+var
+  L: int;
+begin
+  L := length(s);
+  setLength(s, L+1);
+  s[L] := y;
+end;
 {@emit}
 
 function alloc(size: int): Pointer;
diff --git a/nim/nversion.pas b/nim/nversion.pas
index b2ba371f6..20ab5cc77 100755
--- a/nim/nversion.pas
+++ b/nim/nversion.pas
@@ -31,10 +31,10 @@ const
   //cog.outl('VersionMinor = %s;' % ver[1])

   //cog.outl('VersionPatch = %s;' % ver[2])

   //]]]

-  VersionAsString = '0.8.1';
+  VersionAsString = '0.8.2';
   VersionMajor = 0;
   VersionMinor = 8;
-  VersionPatch = 1;
+  VersionPatch = 2;
   //[[[[end]]]]

 

 implementation

diff --git a/nim/options.pas b/nim/options.pas
index d210f3b44..d03a24f2a 100755
--- a/nim/options.pas
+++ b/nim/options.pas
@@ -101,6 +101,7 @@ var
   gCmd: TCommands = cmdNone; // the command
 
   gVerbosity: int; // how verbose the compiler is
+  gNumberOfProcessors: int; // number of processors
 
 function FindFile(const f: string): string;
 
@@ -111,7 +112,6 @@ const
   HtmlExt = 'html';
   TexExt = 'tex';
   IniExt = 'ini';
-  TmplExt = 'tmpl';
   DocConfig = 'nimdoc.cfg';
   DocTexConfig = 'nimdoc.tex.cfg';
 
@@ -124,8 +124,6 @@ function toGeneratedFile(const path, ext: string): string;
 function getPrefixDir: string;
 // gets the application directory
 
-function getFileTrunk(const filename: string): string;
-
 // additional configuration variables:
 var
   gConfigVars: PStringTable;
@@ -185,14 +183,6 @@ begin
   SplitPath(appdir, result, bin);
 end;
 
-function getFileTrunk(const filename: string): string;
-var
-  f, e, dir: string;
-begin
-  splitPath(filename, dir, f);
-  splitFilename(f, result, e);
-end;
-
 function shortenDir(const dir: string): string;
 var 
   prefix: string;
diff --git a/nim/osproc.pas b/nim/osproc.pas
index dd6bd10f0..485daaf67 100755
--- a/nim/osproc.pas
+++ b/nim/osproc.pas
@@ -17,14 +17,42 @@ interface
 

 uses

   nsystem, nos;

-

-function executeCommand(const cmd: string): int;

+
+type
+  TProcessOption = (poEchoCmd, poUseShell, poStdErrToStdOut, poParentStreams);
+  TProcessOptions = set of TProcessOption;
+

+function execCmd(const cmd: string): int;

+function execProcesses(const cmds: array of string;
+                       options: TProcessOptions;
+                       n: int): int;
+
+function countProcessors(): int;
 

 implementation

 

-function executeCommand(const cmd: string): int;

-begin

+function execCmd(const cmd: string): int;

+begin
+  writeln(output, cmd);

   result := executeShellCommand(cmd);

 end;

+
+function execProcesses(const cmds: array of string;
+                       options: TProcessOptions;
+                       n: int): int;
+var
+  i: int;
+begin
+  result := 0;
+  for i := 0 to high(cmds) do begin
+    //if poEchoCmd in options then writeln(output, cmds[i]);
+    result := max(result, execCmd(cmds[i]))
+  end
+end;
+
+function countProcessors(): int;
+begin
+  result := 1;
+end;
 

 end.

diff --git a/nim/pasparse.pas b/nim/pasparse.pas
index c44c735d6..dbfbf0437 100755
--- a/nim/pasparse.pas
+++ b/nim/pasparse.pas
@@ -61,7 +61,7 @@ const
     ('len',          'length'),
     ('setlength',    'setlen')
   );
-  nimReplacements: array [1..34] of TReplaceTuple = (
+  nimReplacements: array [1..35] of TReplaceTuple = (
     ('nimread',      'read'),
     ('nimwrite',     'write'),
     ('nimclosefile', 'close'),
@@ -98,7 +98,8 @@ const
     ('ttextfile', 'tfile'),
     ('tbinaryfile', 'tfile'),
     ('strstart', '0'+''),
-    ('nl', '"\n"')
+    ('nl', '"\n"'),
+    ('tostring', '$'+'')
     {,
     ('NL', '"\n"'),
     ('tabulator', '''\t'''),
@@ -455,7 +456,7 @@ begin
     skipCom(p, result);
     if p.tok.xkind = pxSymbol then begin
       a := result;
-      result := newNodeI(nkQualified, a.info);
+      result := newNodeI(nkDotExpr, a.info);
       addSon(result, a);
       addSon(result, createIdentNodeP(p.tok.ident, p));
       getTok(p);
@@ -1279,6 +1280,13 @@ begin
         noBody := true;
         getTok(p); opt(p, pxSemicolon);
       end;
+      wProcVar: begin
+        // This is a fake for the Nimrod compiler. There is no ``procvar``
+        // directive in Pascal.
+        if result = nil then result := newNodeP(nkPragma, p);
+        addSon(result, newIdentNodeP(getIdent('procvar'), p));
+        getTok(p); opt(p, pxSemicolon);
+      end;
       wVarargs: begin
         if result = nil then result := newNodeP(nkPragma, p);
         addSon(result, newIdentNodeP(getIdent('varargs'), p));
diff --git a/nim/passes.pas b/nim/passes.pas
index 8d961e5d3..c280a75b1 100755
--- a/nim/passes.pas
+++ b/nim/passes.pas
@@ -1,7 +1,7 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
@@ -19,7 +19,7 @@ uses
   nsystem, charsets, strutils,
   lists, options, ast, astalgo, llstream,
   msgs, platform, nos, condsyms, idents, rnimsyn, types,
-  extccomp, nmath, magicsys, nversion, nimsets, pnimsyn, ntime, rodread;
+  extccomp, nmath, magicsys, nversion, nimsets, syntaxes, ntime, rodread;
 
 type
   TPassContext = object(NObject) // the pass's context
@@ -61,7 +61,6 @@ function astNeeded(s: PSym): bool;
 var
   gImportModule: function (const filename: string): PSym;
   gIncludeFile: function (const filename: string): PNode;
-  gIncludeTmplFile: function (const filename: string): PNode;
 
 implementation
 
@@ -165,7 +164,7 @@ end;
 procedure processModule(module: PSym; const filename: string;
                         stream: PLLStream; rd: PRodReader);
 var
-  p: TParser;
+  p: TParsers;
   n: PNode;
   a: TPassContextArray;
   s: PLLStream;
@@ -183,13 +182,13 @@ begin
     else
       s := stream;
     while true do begin
-      openParser(p, filename, s); 
+      openParsers(p, filename, s); 
       while true do begin
         n := parseTopLevelStmt(p);
         if n = nil then break;
         processTopLevelStmt(n, a)
       end;
-      closeParser(p);
+      closeParsers(p);
       if s.kind <> llsStdIn then break;
     end;
     closePasses(a);
diff --git a/nim/pbraces.pas b/nim/pbraces.pas
new file mode 100755
index 000000000..d362fac65
--- /dev/null
+++ b/nim/pbraces.pas
@@ -0,0 +1,1499 @@
+//
+//
+//           The Nimrod Compiler
+//        (c) Copyright 2009 Andreas Rumpf
+//
+//    See the file "copying.txt", included in this
+//    distribution, for details about the copyright.
+//
+unit pbraces;
+
+{$include config.inc}
+
+interface
+
+uses
+  nsystem, llstream, scanner, idents, strutils, ast, msgs, pnimsyn;
+
+function ParseAll(var p: TParser): PNode;
+
+function parseTopLevelStmt(var p: TParser): PNode;
+// implements an iterator. Returns the next top-level statement or nil if end
+// of stream.
+
+implementation
+
+// ------------------- Expression parsing ------------------------------------
+
+function parseExpr(var p: TParser): PNode; forward;
+function parseStmt(var p: TParser): PNode; forward;
+
+function parseTypeDesc(var p: TParser): PNode; forward;
+function parseParamList(var p: TParser): PNode; forward;
+
+function optExpr(var p: TParser): PNode; // [expr]
+begin
+  if (p.tok.tokType <> tkComma) and (p.tok.tokType <> tkBracketRi)
+  and (p.tok.tokType <> tkDotDot) then
+    result := parseExpr(p)
+  else
+    result := nil;
+end;
+
+function dotdotExpr(var p: TParser; first: PNode = nil): PNode;
+begin
+  result := newNodeP(nkRange, p);
+  addSon(result, first);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, optExpr(p));
+end;
+
+function indexExpr(var p: TParser): PNode;
+// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
+var
+  a, b: PNode;
+begin
+  if p.tok.tokType = tkDotDot then
+    result := dotdotExpr(p)
+  else begin
+    a := parseExpr(p);
+    case p.tok.tokType of
+      tkEquals: begin
+        result := newNodeP(nkExprEqExpr, p);
+        addSon(result, a);
+        getTok(p);
+        if p.tok.tokType = tkDotDot then
+          addSon(result, dotdotExpr(p))
+        else begin
+          b := parseExpr(p);
+          if p.tok.tokType = tkDotDot then b := dotdotExpr(p, b);
+          addSon(result, b);
+        end
+      end;
+      tkDotDot: result := dotdotExpr(p, a);
+      else result := a
+    end
+  end
+end;
+
+function indexExprList(var p: TParser; first: PNode): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkBracketExpr, p);
+  addSon(result, first);
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
+  and (p.tok.tokType <> tkSad) do begin
+    a := indexExpr(p);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  optSad(p);
+  eat(p, tkBracketRi);
+end;
+
+function exprColonEqExpr(var p: TParser; kind: TNodeKind;
+                         tok: TTokType): PNode;
+var
+  a: PNode;
+begin
+  a := parseExpr(p);
+  if p.tok.tokType = tok then begin
+    result := newNodeP(kind, p);
+    getTok(p);
+    //optInd(p, result);
+    addSon(result, a);
+    addSon(result, parseExpr(p));
+  end
+  else
+    result := a
+end;
+
+procedure exprListAux(var p: TParser; elemKind: TNodeKind;
+                      endTok, sepTok: TTokType; result: PNode);
+var
+  a: PNode;
+begin
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
+    a := exprColonEqExpr(p, elemKind, sepTok);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  eat(p, endTok);
+end;
+
+function qualifiedIdent(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := parseSymbol(p);
+  if p.tok.tokType = tkDot then begin
+    getTok(p);
+    optInd(p, result);
+    a := result;
+    result := newNodeI(nkDotExpr, a.info);
+    addSon(result, a);
+    addSon(result, parseSymbol(p));
+  end;
+end;
+
+procedure qualifiedIdentListAux(var p: TParser; endTok: TTokType;
+                                result: PNode);
+var
+  a: PNode;
+begin
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
+    a := qualifiedIdent(p);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  eat(p, endTok);
+end;
+
+procedure exprColonEqExprListAux(var p: TParser; elemKind: TNodeKind;
+                                 endTok, sepTok: TTokType; result: PNode);
+var
+  a: PNode;
+begin
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof)
+  and (p.tok.tokType <> tkSad) do begin
+    a := exprColonEqExpr(p, elemKind, sepTok);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  optSad(p);
+  eat(p, endTok);
+end;
+
+function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind;
+                             endTok, sepTok: TTokType): PNode;
+begin
+  result := newNodeP(kind, p);
+  exprColonEqExprListAux(p, elemKind, endTok, sepTok, result);
+end;
+
+function parseCast(var p: TParser): PNode;
+begin
+  result := newNodeP(nkCast, p);
+  getTok(p);
+  eat(p, tkBracketLe);
+  optInd(p, result);
+  addSon(result, parseTypeDesc(p));
+  optSad(p);
+  eat(p, tkBracketRi);
+  eat(p, tkParLe);
+  optInd(p, result);
+  addSon(result, parseExpr(p));
+  optSad(p);
+  eat(p, tkParRi);
+end;
+
+function parseAddr(var p: TParser): PNode;
+begin
+  result := newNodeP(nkAddr, p);
+  getTok(p);
+  eat(p, tkParLe);
+  optInd(p, result);
+  addSon(result, parseExpr(p));
+  optSad(p);
+  eat(p, tkParRi);
+end;
+
+procedure setBaseFlags(n: PNode; base: TNumericalBase);
+begin
+  case base of
+    base10: begin end;
+    base2: include(n.flags, nfBase2);
+    base8: include(n.flags, nfBase8);
+    base16: include(n.flags, nfBase16);
+  end
+end;
+
+function identOrLiteral(var p: TParser): PNode;
+begin
+  case p.tok.tokType of
+    tkSymbol: begin
+      result := newIdentNodeP(p.tok.ident, p);
+      getTok(p)
+    end;
+    tkAccent: result := accExpr(p);
+    // literals
+    tkIntLit: begin
+      result := newIntNodeP(nkIntLit, p.tok.iNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkInt8Lit: begin
+      result := newIntNodeP(nkInt8Lit, p.tok.iNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkInt16Lit: begin
+      result := newIntNodeP(nkInt16Lit, p.tok.iNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkInt32Lit: begin
+      result := newIntNodeP(nkInt32Lit, p.tok.iNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkInt64Lit: begin
+      result := newIntNodeP(nkInt64Lit, p.tok.iNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkFloatLit: begin
+      result := newFloatNodeP(nkFloatLit, p.tok.fNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkFloat32Lit: begin
+      result := newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkFloat64Lit: begin
+      result := newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p);
+      setBaseFlags(result, p.tok.base);
+      getTok(p);
+    end;
+    tkStrLit: begin
+      result := newStrNodeP(nkStrLit, p.tok.literal, p);
+      getTok(p);
+    end;
+    tkRStrLit: begin
+      result := newStrNodeP(nkRStrLit, p.tok.literal, p);
+      getTok(p);
+    end;
+    tkTripleStrLit: begin
+      result := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
+      getTok(p);
+    end;
+    tkCallRStrLit: begin
+      result := newNodeP(nkCallStrLit, p);
+      addSon(result, newIdentNodeP(p.tok.ident, p));
+      addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p));
+      getTok(p);
+    end;
+    tkCallTripleStrLit: begin
+      result := newNodeP(nkCallStrLit, p);
+      addSon(result, newIdentNodeP(p.tok.ident, p));
+      addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p));
+      getTok(p);
+    end;
+    tkCharLit: begin
+      result := newIntNodeP(nkCharLit, ord(p.tok.literal[strStart]), p);
+      getTok(p);
+    end;
+    tkNil: begin
+      result := newNodeP(nkNilLit, p);
+      getTok(p);
+    end;
+    tkParLe: begin // () constructor
+      result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi,
+                                    tkColon);
+    end;
+    tkCurlyLe: begin // {} constructor
+      result := exprColonEqExprList(p, nkCurly, nkRange, tkCurlyRi, tkDotDot);
+    end;
+    tkBracketLe: begin // [] constructor
+      result := exprColonEqExprList(p, nkBracket, nkExprColonExpr, tkBracketRi,
+                                    tkColon);
+    end;
+    tkCast: result := parseCast(p);
+    tkAddr: result := parseAddr(p);
+    else begin
+      parMessage(p, errExprExpected, tokToStr(p.tok));
+      getTok(p); // we must consume a token here to prevend endless loops!
+      result := nil
+    end
+  end
+end;
+
+function primary(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  // prefix operator?
+  if (p.tok.tokType = tkNot) or (p.tok.tokType = tkOpr) then begin
+    result := newNodeP(nkPrefix, p);
+    a := newIdentNodeP(p.tok.ident, p);
+    addSon(result, a);
+    getTok(p);
+    optInd(p, a);
+    addSon(result, primary(p));
+    exit
+  end
+  else if p.tok.tokType = tkBind then begin
+    result := newNodeP(nkBind, p);
+    getTok(p);
+    optInd(p, result);
+    addSon(result, primary(p));
+    exit
+  end;
+  result := identOrLiteral(p);
+  while true do begin
+    case p.tok.tokType of
+      tkParLe: begin
+        a := result;
+        result := newNodeP(nkCall, p);
+        addSon(result, a);
+        exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
+      end;
+      tkDot: begin
+        a := result;
+        result := newNodeP(nkDotExpr, p);
+        addSon(result, a);
+        getTok(p); // skip '.'
+        optInd(p, result);
+        addSon(result, parseSymbol(p));
+      end;
+      tkHat: begin
+        a := result;
+        result := newNodeP(nkDerefExpr, p);
+        addSon(result, a);
+        getTok(p);
+      end;
+      tkBracketLe: result := indexExprList(p, result);
+      else break
+    end
+  end
+end;
+
+function lowestExprAux(var p: TParser; out v: PNode; limit: int): PToken;
+var
+  op, nextop: PToken;
+  opPred: int;
+  v2, node, opNode: PNode;
+begin
+  v := primary(p);
+  // expand while operators have priorities higher than 'limit'
+  op := p.tok;
+  opPred := getPrecedence(p.tok);
+  while (opPred > limit) do begin
+    node := newNodeP(nkInfix, p);
+    opNode := newIdentNodeP(op.ident, p);
+    // skip operator:
+    getTok(p);
+    optInd(p, opNode);
+
+    // read sub-expression with higher priority
+    nextop := lowestExprAux(p, v2, opPred);
+    addSon(node, opNode);
+    addSon(node, v);
+    addSon(node, v2);
+    v := node;
+    op := nextop;
+    opPred := getPrecedence(nextop);
+  end;
+  result := op;  // return first untreated operator
+end;
+
+function lowestExpr(var p: TParser): PNode;
+begin
+{@discard} lowestExprAux(p, result, -1);
+end;
+
+function parseIfExpr(var p: TParser): PNode;
+// if (expr) expr else expr
+var
+  branch: PNode;
+begin
+  result := newNodeP(nkIfExpr, p);
+  while true do begin
+    getTok(p); // skip `if`, `elif`
+    branch := newNodeP(nkElifExpr, p);
+    eat(p, tkParLe);
+    addSon(branch, parseExpr(p));
+    eat(p, tkParRi);
+    addSon(branch, parseExpr(p));
+    addSon(result, branch);
+    if p.tok.tokType <> tkElif then break
+  end;
+  branch := newNodeP(nkElseExpr, p);
+  eat(p, tkElse);
+  addSon(branch, parseExpr(p));
+  addSon(result, branch);
+end;
+
+function parsePragma(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkPragma, p);
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType <> tkCurlyDotRi) and (p.tok.tokType <> tkCurlyRi)
+  and (p.tok.tokType <> tkEof) and (p.tok.tokType <> tkSad) do begin
+    a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
+    addSon(result, a);
+    if p.tok.tokType = tkComma then begin
+      getTok(p);
+      optInd(p, a)
+    end
+  end;
+  optSad(p);
+  if (p.tok.tokType = tkCurlyDotRi) or (p.tok.tokType = tkCurlyRi) then
+    getTok(p)
+  else
+    parMessage(p, errTokenExpected, '.}');
+end;
+
+function identVis(var p: TParser): PNode; // identifier with visability
+var
+  a: PNode;
+begin
+  a := parseSymbol(p);
+  if p.tok.tokType = tkOpr then begin
+    result := newNodeP(nkPostfix, p);
+    addSon(result, newIdentNodeP(p.tok.ident, p));
+    addSon(result, a);
+    getTok(p);
+  end
+  else
+    result := a;
+end;
+
+function identWithPragma(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  a := identVis(p);
+  if p.tok.tokType = tkCurlyDotLe then begin
+    result := newNodeP(nkPragmaExpr, p);
+    addSon(result, a);
+    addSon(result, parsePragma(p));
+  end
+  else
+    result := a
+end;
+
+type
+  TDeclaredIdentFlag = (
+    withPragma,                // identifier may have pragma
+    withBothOptional           // both ':' and '=' parts are optional
+  );
+  TDeclaredIdentFlags = set of TDeclaredIdentFlag;
+
+function parseIdentColonEquals(var p: TParser;
+                               flags: TDeclaredIdentFlags): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkIdentDefs, p);
+  while true do begin
+    case p.tok.tokType of
+      tkSymbol, tkAccent: begin
+        if withPragma in flags then
+          a := identWithPragma(p)
+        else
+          a := parseSymbol(p);
+        if a = nil then exit;
+      end;
+      else break;
+    end;
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  if p.tok.tokType = tkColon then begin
+    getTok(p); optInd(p, result);
+    addSon(result, parseTypeDesc(p));
+  end
+  else begin
+    addSon(result, nil);
+    if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
+      parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
+  end;
+  if p.tok.tokType = tkEquals then begin
+    getTok(p); optInd(p, result);
+    addSon(result, parseExpr(p));
+  end
+  else
+    addSon(result, nil);
+end;
+
+function parseTuple(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkTupleTy, p);
+  getTok(p);
+  eat(p, tkBracketLe);
+  optInd(p, result);
+  while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
+    a := parseIdentColonEquals(p, {@set}[]);
+    addSon(result, a);        
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  optSad(p);
+  eat(p, tkBracketRi);
+end;
+
+function parseParamList(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkFormalParams, p);
+  addSon(result, nil); // return type
+  if p.tok.tokType = tkParLe then begin
+    getTok(p);
+    optInd(p, result);
+    while true do begin
+      case p.tok.tokType of
+        tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
+        tkParRi: break;
+        else begin parMessage(p, errTokenExpected, ')'+''); break; end;
+      end;
+      addSon(result, a);
+      if p.tok.tokType <> tkComma then break;
+      getTok(p);
+      optInd(p, a)
+    end;
+    optSad(p);
+    eat(p, tkParRi);
+  end;
+  if p.tok.tokType = tkColon then begin
+    getTok(p);
+    optInd(p, result);
+    result.sons[0] := parseTypeDesc(p)
+  end
+end;
+
+function parseProcExpr(var p: TParser; isExpr: bool): PNode;
+// either a proc type or a anonymous proc
+var
+  pragmas, params: PNode;
+  info: TLineInfo;
+begin
+  info := parLineInfo(p);
+  getTok(p);
+  params := parseParamList(p);
+  if p.tok.tokType = tkCurlyDotLe then pragmas := parsePragma(p)
+  else pragmas := nil;
+  if (p.tok.tokType = tkCurlyLe) and isExpr then begin
+    result := newNodeI(nkLambda, info);
+    addSon(result, nil); // no name part
+    addSon(result, nil); // no generic parameters
+    addSon(result, params);
+    addSon(result, pragmas);
+    //getTok(p); skipComment(p, result);
+    addSon(result, parseStmt(p));
+  end
+  else begin
+    result := newNodeI(nkProcTy, info);
+    addSon(result, params);
+    addSon(result, pragmas);
+  end
+end;
+
+function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, parseTypeDesc(p));
+end;
+
+function parseExpr(var p: TParser): PNode;
+(*
+expr ::= lowestExpr
+     | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
+     | 'var' expr
+     | 'ref' expr
+     | 'ptr' expr
+     | 'type' expr
+     | 'tuple' tupleDesc
+     | 'proc' paramList [pragma] ['=' stmt] 
+*)
+begin
+  case p.tok.toktype of
+    tkVar:   result := parseTypeDescKAux(p, nkVarTy);
+    tkRef:   result := parseTypeDescKAux(p, nkRefTy);
+    tkPtr:   result := parseTypeDescKAux(p, nkPtrTy);
+    tkType:  result := parseTypeDescKAux(p, nkTypeOfExpr);
+    tkTuple: result := parseTuple(p);
+    tkProc:  result := parseProcExpr(p, true);
+    tkIf:    result := parseIfExpr(p);
+    else     result := lowestExpr(p);
+  end
+end;
+
+function parseTypeDesc(var p: TParser): PNode;
+begin
+  if p.tok.toktype = tkProc then result := parseProcExpr(p, false)
+  else result := parseExpr(p);
+end;
+
+// ---------------------- statement parser ------------------------------------
+function isExprStart(const p: TParser): bool;
+begin
+  case p.tok.tokType of
+    tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
+    tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit,
+    tkVar, tkRef, tkPtr, tkTuple, tkType: result := true;
+    else result := false;
+  end;
+end;
+
+function parseExprStmt(var p: TParser): PNode;
+var
+  a, b, e: PNode;
+begin
+  a := lowestExpr(p);
+  if p.tok.tokType = tkEquals then begin
+    getTok(p);
+    optInd(p, result);
+    b := parseExpr(p);
+    result := newNodeI(nkAsgn, a.info);
+    addSon(result, a);
+    addSon(result, b);
+  end
+  else begin
+    result := newNodeP(nkCommand, p);
+    result.info := a.info;
+    addSon(result, a);
+    while true do begin
+      if not isExprStart(p) then break;
+      e := parseExpr(p);
+      addSon(result, e);
+      if p.tok.tokType <> tkComma then break;
+      getTok(p);
+      optInd(p, a);
+    end;
+    if sonsLen(result) <= 1 then result := a
+    else a := result;
+    if p.tok.tokType = tkCurlyLe then begin // macro statement
+      result := newNodeP(nkMacroStmt, p);
+      result.info := a.info;
+      addSon(result, a);
+      getTok(p);
+      skipComment(p, result);
+      if (p.tok.tokType = tkInd)
+           or not (p.tok.TokType in [tkOf, tkElif, tkElse, tkExcept]) then
+        addSon(result, parseStmt(p));
+      while true do begin
+        if p.tok.tokType = tkSad then getTok(p);
+        case p.tok.tokType of
+          tkOf: begin
+            b := newNodeP(nkOfBranch, p);
+            exprListAux(p, nkRange, tkCurlyLe, tkDotDot, b);
+          end;
+          tkElif: begin
+            b := newNodeP(nkElifBranch, p);
+            getTok(p);
+            optInd(p, b);
+            addSon(b, parseExpr(p));
+            eat(p, tkCurlyLe);
+          end;
+          tkExcept: begin
+            b := newNodeP(nkExceptBranch, p);
+            qualifiedIdentListAux(p, tkCurlyLe, b);
+            skipComment(p, b);
+          end;
+          tkElse: begin
+            b := newNodeP(nkElse, p);
+            getTok(p);
+            eat(p, tkCurlyLe);
+          end;
+          else break;
+        end;
+        addSon(b, parseStmt(p));
+        eat(p, tkCurlyRi);
+        addSon(result, b);
+        if b.kind = nkElse then break;
+      end;
+      eat(p, tkCurlyRi);
+    end
+  end
+end;
+
+function parseImportStmt(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkImportStmt, p);
+  getTok(p); // skip `import`
+  optInd(p, result);
+  while true do begin
+    case p.tok.tokType of
+      tkEof, tkSad, tkDed: break;
+      tkSymbol, tkAccent: a := parseSymbol(p);
+      tkRStrLit: begin
+        a := newStrNodeP(nkRStrLit, p.tok.literal, p);
+        getTok(p)
+      end;
+      tkStrLit: begin
+        a := newStrNodeP(nkStrLit, p.tok.literal, p);
+        getTok(p);
+      end;
+      tkTripleStrLit: begin
+        a := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
+        getTok(p)
+      end;
+      else begin
+        parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+        break
+      end
+    end;
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+end;
+
+function parseIncludeStmt(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkIncludeStmt, p);
+  getTok(p); // skip `include`
+  optInd(p, result);
+  while true do begin
+    case p.tok.tokType of
+      tkEof, tkSad, tkDed: break;
+      tkSymbol, tkAccent:   a := parseSymbol(p);
+      tkRStrLit:  begin
+        a := newStrNodeP(nkRStrLit, p.tok.literal, p);
+        getTok(p)
+      end;
+      tkStrLit: begin
+        a := newStrNodeP(nkStrLit, p.tok.literal, p);
+        getTok(p);
+      end;
+      tkTripleStrLit: begin
+        a := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
+        getTok(p)
+      end;
+      else begin
+        parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+        break
+      end;
+    end;
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+end;
+
+function parseFromStmt(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkFromStmt, p);
+  getTok(p); // skip `from`
+  optInd(p, result);
+  case p.tok.tokType of
+    tkSymbol, tkAccent: a := parseSymbol(p);
+    tkRStrLit:  begin
+      a := newStrNodeP(nkRStrLit, p.tok.literal, p);
+      getTok(p)
+    end;
+    tkStrLit: begin
+      a := newStrNodeP(nkStrLit, p.tok.literal, p);
+      getTok(p);
+    end;
+    tkTripleStrLit: begin
+      a := newStrNodeP(nkTripleStrLit, p.tok.literal, p);
+      getTok(p)
+    end;
+    else begin
+      parMessage(p, errIdentifierExpected, tokToStr(p.tok)); exit
+    end
+  end;
+  addSon(result, a);
+  //optInd(p, a);
+  eat(p, tkImport);
+  optInd(p, result);
+  while true do begin
+    case p.tok.tokType of
+      tkEof, tkSad, tkDed: break;
+      tkSymbol, tkAccent: a := parseSymbol(p);
+      else begin
+        parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+        break
+      end;
+    end;
+    //optInd(p, a);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+end;
+
+function parseReturnOrRaise(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  case p.tok.tokType of
+    tkEof, tkSad, tkDed: addSon(result, nil);
+    else addSon(result, parseExpr(p));
+  end;
+end;
+
+function parseYieldOrDiscard(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, parseExpr(p));
+end;
+
+function parseBreakOrContinue(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  case p.tok.tokType of
+    tkEof, tkSad, tkDed: addSon(result, nil);
+    else addSon(result, parseSymbol(p));
+  end;
+end;
+
+function parseIfOrWhen(var p: TParser; kind: TNodeKind): PNode;
+var
+  branch: PNode;
+begin
+  result := newNodeP(kind, p);
+  while true do begin
+    getTok(p); // skip `if`, `when`, `elif`
+    branch := newNodeP(nkElifBranch, p);
+    optInd(p, branch);
+    eat(p, tkParLe);
+    addSon(branch, parseExpr(p));
+    eat(p, tkParRi);
+    skipComment(p, branch);
+    addSon(branch, parseStmt(p));
+    skipComment(p, branch);
+    addSon(result, branch);
+    if p.tok.tokType <> tkElif then break
+  end;
+  if p.tok.tokType = tkElse then begin
+    branch := newNodeP(nkElse, p);
+    eat(p, tkElse);
+    skipComment(p, branch);
+    addSon(branch, parseStmt(p));
+    addSon(result, branch);
+  end
+end;
+
+function parseWhile(var p: TParser): PNode;
+begin
+  result := newNodeP(nkWhileStmt, p);
+  getTok(p);
+  optInd(p, result);
+  eat(p, tkParLe);
+  addSon(result, parseExpr(p));
+  eat(p, tkParRi);
+  skipComment(p, result);
+  addSon(result, parseStmt(p));
+end;
+
+function parseCase(var p: TParser): PNode;
+var
+  b: PNode;
+  inElif: bool;
+begin
+  result := newNodeP(nkCaseStmt, p);
+  getTok(p);
+  eat(p, tkParLe);
+  addSon(result, parseExpr(p));
+  eat(p, tkParRi);
+  skipComment(p, result);
+  inElif := false;
+  while true do begin
+    if p.tok.tokType = tkSad then getTok(p);
+    case p.tok.tokType of
+      tkOf: begin
+        if inElif then break;
+        b := newNodeP(nkOfBranch, p);
+        exprListAux(p, nkRange, tkColon, tkDotDot, b);
+      end;
+      tkElif: begin
+        inElif := true;
+        b := newNodeP(nkElifBranch, p);
+        getTok(p);
+        optInd(p, b);
+        addSon(b, parseExpr(p));
+        eat(p, tkColon);
+      end;
+      tkElse: begin
+        b := newNodeP(nkElse, p);
+        getTok(p);
+        eat(p, tkColon);
+      end;
+      else break;
+    end;
+    skipComment(p, b);
+    addSon(b, parseStmt(p));
+    addSon(result, b);
+    if b.kind = nkElse then break;
+  end
+end;
+
+function parseTry(var p: TParser): PNode;
+var
+  b: PNode;
+begin
+  result := newNodeP(nkTryStmt, p);
+  getTok(p);
+  eat(p, tkColon);
+  skipComment(p, result);
+  addSon(result, parseStmt(p));
+  b := nil;
+  while true do begin
+    if p.tok.tokType = tkSad then getTok(p);
+    case p.tok.tokType of
+      tkExcept: begin
+        b := newNodeP(nkExceptBranch, p);
+        qualifiedIdentListAux(p, tkColon, b);
+      end;
+      tkFinally: begin
+        b := newNodeP(nkFinally, p);
+        getTok(p);
+        eat(p, tkColon);
+      end;
+      else break;
+    end;
+    skipComment(p, b);
+    addSon(b, parseStmt(p));
+    addSon(result, b);
+    if b.kind = nkFinally then break;
+  end;
+  if b = nil then parMessage(p, errTokenExpected, 'except');
+end;
+
+function parseFor(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkForStmt, p);
+  getTok(p);
+  optInd(p, result);
+  a := parseSymbol(p);
+  addSon(result, a);
+  while p.tok.tokType = tkComma do begin
+    getTok(p);
+    optInd(p, a);
+    a := parseSymbol(p);
+    addSon(result, a);    
+  end;
+  eat(p, tkIn);
+  addSon(result, exprColonEqExpr(p, nkRange, tkDotDot));
+  eat(p, tkColon);
+  skipComment(p, result);
+  addSon(result, parseStmt(p))
+end;
+
+function parseBlock(var p: TParser): PNode;
+begin
+  result := newNodeP(nkBlockStmt, p);
+  getTok(p);
+  optInd(p, result);
+  case p.tok.tokType of
+    tkEof, tkSad, tkDed, tkColon: addSon(result, nil);
+    else addSon(result, parseSymbol(p));
+  end;
+  eat(p, tkColon);
+  skipComment(p, result);
+  addSon(result, parseStmt(p));
+end;
+
+function parseAsm(var p: TParser): PNode;
+begin
+  result := newNodeP(nkAsmStmt, p);
+  getTok(p);
+  optInd(p, result);
+  if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
+  else addSon(result, nil);
+  case p.tok.tokType of
+    tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p));
+    tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p));
+    tkTripleStrLit:
+      addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p));
+    else begin
+      parMessage(p, errStringLiteralExpected);
+      addSon(result, nil); exit
+    end;
+  end;
+  getTok(p);
+end;
+
+function parseGenericParamList(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkGenericParams, p);
+  getTok(p);
+  optInd(p, result);
+  while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
+    a := parseIdentColonEquals(p, {@set}[withBothOptional]);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  optSad(p);
+  eat(p, tkBracketRi);
+end;
+
+function parseRoutine(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, identVis(p));
+  if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p))
+  else addSon(result, nil);
+  addSon(result, parseParamList(p));
+  if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
+  else addSon(result, nil);
+  if p.tok.tokType = tkEquals then begin
+    getTok(p); skipComment(p, result);
+    addSon(result, parseStmt(p));
+  end
+  else
+    addSon(result, nil);
+  indAndComment(p, result); // XXX: document this in the grammar!
+end;
+
+function newCommentStmt(var p: TParser): PNode;
+begin
+  result := newNodeP(nkCommentStmt, p);
+  result.info.line := result.info.line - int16(1);
+end;
+
+type
+  TDefParser = function (var p: TParser): PNode;
+
+function parseSection(var p: TParser; kind: TNodeKind;
+                      defparser: TDefParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  skipComment(p, result);
+  case p.tok.tokType of
+    tkInd: begin
+      pushInd(p.lex^, p.tok.indent);
+      getTok(p); skipComment(p, result);
+      while true do begin
+        case p.tok.tokType of
+          tkSad: getTok(p);
+          tkSymbol, tkAccent: begin
+            a := defparser(p);
+            skipComment(p, a);
+            addSon(result, a);
+          end;
+          tkDed: begin getTok(p); break end;
+          tkEof: break; // BUGFIX
+          tkComment: begin
+            a := newCommentStmt(p);
+            skipComment(p, a);
+            addSon(result, a);
+          end;
+          else begin
+            parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+            break
+          end
+        end
+      end;
+      popInd(p.lex^);
+    end;
+    tkSymbol, tkAccent, tkParLe: begin
+      // tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
+      addSon(result, defparser(p));
+    end
+    else parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+  end
+end;
+
+function parseConstant(var p: TParser): PNode;
+begin
+  result := newNodeP(nkConstDef, p);
+  addSon(result, identWithPragma(p));
+  if p.tok.tokType = tkColon then begin
+    getTok(p); optInd(p, result);
+    addSon(result, parseTypeDesc(p));
+  end
+  else
+    addSon(result, nil);
+  eat(p, tkEquals);
+  optInd(p, result);
+  addSon(result, parseExpr(p));
+  indAndComment(p, result); // XXX: special extension!
+end;
+
+function parseEnum(var p: TParser): PNode;
+var
+  a, b: PNode;
+begin
+  result := newNodeP(nkEnumTy, p);
+  a := nil;
+  getTok(p);
+  optInd(p, result);
+  if p.tok.tokType = tkOf then begin
+    a := newNodeP(nkOfInherit, p);
+    getTok(p); optInd(p, a);
+    addSon(a, parseTypeDesc(p));
+    addSon(result, a)
+  end
+  else addSon(result, nil);
+
+  while true do begin
+    case p.tok.tokType of
+      tkEof, tkSad, tkDed: break;
+      else a := parseSymbol(p);
+    end;
+    optInd(p, a);
+    if p.tok.tokType = tkEquals then begin
+      getTok(p);
+      optInd(p, a);
+      b := a;
+      a := newNodeP(nkEnumFieldDef, p);
+      addSon(a, b);
+      addSon(a, parseExpr(p));
+      skipComment(p, a);
+    end;
+    if p.tok.tokType = tkComma then begin
+      getTok(p);
+      optInd(p, a)
+    end;
+    addSon(result, a);
+  end
+end;
+
+function parseObjectPart(var p: TParser): PNode; forward;
+
+function parseObjectWhen(var p: TParser): PNode;
+var
+  branch: PNode;
+begin
+  result := newNodeP(nkRecWhen, p);
+  while true do begin
+    getTok(p); // skip `when`, `elif`
+    branch := newNodeP(nkElifBranch, p);
+    optInd(p, branch);
+    addSon(branch, parseExpr(p));
+    eat(p, tkColon);
+    skipComment(p, branch);
+    addSon(branch, parseObjectPart(p));
+    skipComment(p, branch);
+    addSon(result, branch);
+    if p.tok.tokType <> tkElif then break
+  end;
+  if p.tok.tokType = tkElse then begin
+    branch := newNodeP(nkElse, p);
+    eat(p, tkElse); eat(p, tkColon);
+    skipComment(p, branch);
+    addSon(branch, parseObjectPart(p));
+    addSon(result, branch);
+  end
+end;
+
+function parseObjectCase(var p: TParser): PNode;
+var
+  a, b: PNode;
+begin
+  result := newNodeP(nkRecCase, p);
+  getTok(p);
+  a := newNodeP(nkIdentDefs, p);
+  addSon(a, identWithPragma(p));
+  eat(p, tkColon);
+  addSon(a, parseTypeDesc(p));
+  addSon(a, nil);
+  addSon(result, a);
+  skipComment(p, result);
+  while true do begin
+    if p.tok.tokType = tkSad then getTok(p);
+    case p.tok.tokType of
+      tkOf: begin
+        b := newNodeP(nkOfBranch, p);
+        exprListAux(p, nkRange, tkColon, tkDotDot, b);
+      end;
+      tkElse: begin
+        b := newNodeP(nkElse, p);
+        getTok(p);
+        eat(p, tkColon);
+      end;
+      else break;
+    end;
+    skipComment(p, b);
+    addSon(b, parseObjectPart(p));
+    addSon(result, b);
+    if b.kind = nkElse then break;
+  end
+end;
+
+function parseObjectPart(var p: TParser): PNode;
+begin
+  case p.tok.tokType of
+    tkInd: begin
+      result := newNodeP(nkRecList, p);
+      pushInd(p.lex^, p.tok.indent);
+      getTok(p); skipComment(p, result);
+      while true do begin
+        case p.tok.tokType of
+          tkSad: getTok(p);
+          tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin
+            addSon(result, parseObjectPart(p));
+          end;
+          tkDed: begin getTok(p); break end;
+          tkEof: break;
+          else begin
+            parMessage(p, errIdentifierExpected, tokToStr(p.tok));
+            break
+          end
+        end
+      end;
+      popInd(p.lex^);
+    end;
+    tkWhen: result := parseObjectWhen(p);
+    tkCase: result := parseObjectCase(p);
+    tkSymbol, tkAccent: begin
+      result := parseIdentColonEquals(p, {@set}[withPragma]);
+      skipComment(p, result);
+    end;
+    tkNil: begin
+      result := newNodeP(nkNilLit, p);
+      getTok(p);
+    end;
+    else result := nil
+  end
+end;
+
+function parseObject(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkObjectTy, p);
+  getTok(p);
+  if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
+  else addSon(result, nil);
+  if p.tok.tokType = tkOf then begin
+    a := newNodeP(nkOfInherit, p);
+    getTok(p);
+    addSon(a, parseTypeDesc(p));
+    addSon(result, a);
+  end
+  else addSon(result, nil);
+  skipComment(p, result);
+  addSon(result, parseObjectPart(p));
+end;
+
+function parseDistinct(var p: TParser): PNode;
+begin
+  result := newNodeP(nkDistinctTy, p);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, parseTypeDesc(p));
+end;
+
+function parseTypeDef(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkTypeDef, p);
+  addSon(result, identWithPragma(p));
+  if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p))
+  else addSon(result, nil);
+  if p.tok.tokType = tkEquals then begin
+    getTok(p); optInd(p, result);
+    case p.tok.tokType of
+      tkObject: a := parseObject(p);
+      tkEnum: a := parseEnum(p);
+      tkDistinct: a := parseDistinct(p);
+      else a := parseTypeDesc(p);
+    end;
+    addSon(result, a);
+  end
+  else
+    addSon(result, nil);
+  indAndComment(p, result); // special extension!
+end;
+
+function parseVarTuple(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkVarTuple, p);
+  getTok(p); // skip '('
+  optInd(p, result);
+  while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
+    a := identWithPragma(p);
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  addSon(result, nil); // no type desc
+  optSad(p);
+  eat(p, tkParRi);
+  eat(p, tkEquals);
+  optInd(p, result);
+  addSon(result, parseExpr(p));
+end;
+
+function parseVariable(var p: TParser): PNode;
+begin
+  if p.tok.tokType = tkParLe then 
+    result := parseVarTuple(p)
+  else
+    result := parseIdentColonEquals(p, {@set}[withPragma]);
+  indAndComment(p, result); // special extension!
+end;
+
+function simpleStmt(var p: TParser): PNode;
+begin
+  case p.tok.tokType of
+    tkReturn:   result := parseReturnOrRaise(p, nkReturnStmt);
+    tkRaise:    result := parseReturnOrRaise(p, nkRaiseStmt);
+    tkYield:    result := parseYieldOrDiscard(p, nkYieldStmt);
+    tkDiscard:  result := parseYieldOrDiscard(p, nkDiscardStmt);
+    tkBreak:    result := parseBreakOrContinue(p, nkBreakStmt);
+    tkContinue: result := parseBreakOrContinue(p, nkContinueStmt);
+    tkCurlyDotLe: result := parsePragma(p);
+    tkImport: result := parseImportStmt(p);
+    tkFrom: result := parseFromStmt(p);
+    tkInclude: result := parseIncludeStmt(p);
+    tkComment: result := newCommentStmt(p);
+    else begin
+      if isExprStart(p) then 
+        result := parseExprStmt(p)
+      else 
+        result := nil;
+    end
+  end;
+  if result <> nil then
+    skipComment(p, result);
+end;
+
+function complexOrSimpleStmt(var p: TParser): PNode;
+begin
+  case p.tok.tokType of
+    tkIf:        result := parseIfOrWhen(p, nkIfStmt);
+    tkWhile:     result := parseWhile(p);
+    tkCase:      result := parseCase(p);
+    tkTry:       result := parseTry(p);
+    tkFor:       result := parseFor(p);
+    tkBlock:     result := parseBlock(p);
+    tkAsm:       result := parseAsm(p);
+    tkProc:      result := parseRoutine(p, nkProcDef);
+    tkMethod:    result := parseRoutine(p, nkMethodDef);
+    tkIterator:  result := parseRoutine(p, nkIteratorDef);
+    tkMacro:     result := parseRoutine(p, nkMacroDef);
+    tkTemplate:  result := parseRoutine(p, nkTemplateDef);
+    tkConverter: result := parseRoutine(p, nkConverterDef);
+    tkType:      result := parseSection(p, nkTypeSection, parseTypeDef);
+    tkConst:     result := parseSection(p, nkConstSection, parseConstant);
+    tkWhen:      result := parseIfOrWhen(p, nkWhenStmt);
+    tkVar:       result := parseSection(p, nkVarSection, parseVariable);
+    else         result := simpleStmt(p);
+  end
+end;
+
+function parseStmt(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  if p.tok.tokType = tkInd then begin
+    result := newNodeP(nkStmtList, p);
+    pushInd(p.lex^, p.tok.indent);
+    getTok(p);
+    while true do begin
+      case p.tok.tokType of
+        tkSad: getTok(p);
+        tkEof: break;
+        tkDed: begin getTok(p); break end;
+        else begin
+          a := complexOrSimpleStmt(p);
+          if a = nil then break;
+          addSon(result, a);
+        end
+      end
+    end;
+    popInd(p.lex^);
+  end
+  else begin
+    // the case statement is only needed for better error messages:
+    case p.tok.tokType of
+      tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm,
+      tkProc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: begin
+        parMessage(p, errComplexStmtRequiresInd);
+        result := nil
+      end
+      else begin
+        result := simpleStmt(p);
+        if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
+        if p.tok.tokType = tkSad then getTok(p);
+      end
+    end
+  end
+end;
+
+function parseAll(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkStmtList, p);
+  while true do begin
+    case p.tok.tokType of
+      tkSad: getTok(p);
+      tkDed, tkInd: parMessage(p, errInvalidIndentation);
+      tkEof: break;
+      else begin
+        a := complexOrSimpleStmt(p);
+        if a = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
+        addSon(result, a);
+      end
+    end
+  end
+end;
+
+function parseTopLevelStmt(var p: TParser): PNode;
+begin
+  result := nil;
+  while true do begin
+    case p.tok.tokType of
+      tkSad: getTok(p);
+      tkDed, tkInd: begin
+        parMessage(p, errInvalidIndentation);
+        break;
+      end;
+      tkEof: break;
+      else begin
+        result := complexOrSimpleStmt(p);
+        if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
+        break
+      end
+    end
+  end
+end;
+
+end.
diff --git a/nim/platform.pas b/nim/platform.pas
index 9f8d30f60..c2fa711b9 100755
--- a/nim/platform.pas
+++ b/nim/platform.pas
@@ -1,7 +1,7 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
@@ -59,9 +59,8 @@ type
   TInfoOS = record{@tuple}
     name: string;
     parDir: string;
-    dllExt: string;
+    dllFrmt: string;
     altDirSep: string;
-    dllPrefix: string;
     objExt: string;
     newLine: string;
     pathSep: string;
@@ -77,9 +76,8 @@ const
   (
     name: 'DOS';
     parDir: '..';
-    dllExt: '.dll';
+    dllFrmt: '$1.dll';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '.obj';
     newLine: #13#10;
     pathSep: ';'+'';
@@ -93,9 +91,8 @@ const
   (
     name: 'Windows';
     parDir: '..';
-    dllExt: '.dll';
+    dllFrmt: '$1.dll';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '.obj';
     newLine: #13#10;
     pathSep: ';'+'';
@@ -109,9 +106,8 @@ const
   (
     name: 'OS2';
     parDir: '..';
-    dllExt: '.dll';
+    dllFrmt: '$1.dll';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '.obj';
     newLine: #13#10;
     pathSep: ';'+'';
@@ -125,9 +121,8 @@ const
   (
     name: 'Linux';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -141,9 +136,8 @@ const
   (
     name: 'MorphOS';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -157,9 +151,8 @@ const
   (
     name: 'SkyOS';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -173,9 +166,8 @@ const
   (
     name: 'Solaris';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -189,9 +181,8 @@ const
   (
     name: 'Irix';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -205,9 +196,8 @@ const
   (
     name: 'NetBSD';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -221,9 +211,8 @@ const
   (
     name: 'FreeBSD';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -237,9 +226,8 @@ const
   (
     name: 'OpenBSD';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -253,9 +241,8 @@ const
   (
     name: 'AIX';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -269,9 +256,8 @@ const
   (
     name: 'PalmOS';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -285,9 +271,8 @@ const
   (
     name: 'QNX';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -301,9 +286,8 @@ const
   (
     name: 'Amiga';
     parDir: '..';
-    dllExt: '.library';
+    dllFrmt: '$1.library';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -317,9 +301,8 @@ const
   (
     name: 'Atari';
     parDir: '..';
-    dllExt: '.dll';
+    dllFrmt: '$1.dll';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -333,9 +316,8 @@ const
   (
     name: 'Netware';
     parDir: '..';
-    dllExt: '.nlm';
+    dllFrmt: '$1.nlm';
     altDirSep: '/'+'';
-    dllPrefix: '';
     objExt: '';
     newLine: #13#10;
     pathSep: ':'+'';
@@ -349,9 +331,8 @@ const
   (
     name: 'MacOS';
     parDir: '::';
-    dllExt: 'Lib';
+    dllFrmt: '$1Lib';
     altDirSep: ':'+'';
-    dllPrefix: '';
     objExt: '.o';
     newLine: #13+'';
     pathSep: ','+'';
@@ -365,9 +346,8 @@ const
   (
     name: 'MacOSX';
     parDir: '..';
-    dllExt: '.dylib';
+    dllFrmt: 'lib$1.dylib';
     altDirSep: ':'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -381,9 +361,8 @@ const
   (
     name: 'EcmaScript';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
@@ -397,9 +376,8 @@ const
   (
     name: 'NimrodVM';
     parDir: '..';
-    dllExt: '.so';
+    dllFrmt: 'lib$1.so';
     altDirSep: '/'+'';
-    dllPrefix: 'lib';
     objExt: '.o';
     newLine: #10+'';
     pathSep: ':'+'';
diff --git a/nim/pnimsyn.pas b/nim/pnimsyn.pas
index 724a85a38..bf964fda1 100755
--- a/nim/pnimsyn.pas
+++ b/nim/pnimsyn.pas
@@ -22,7 +22,7 @@ interface
 uses
   nsystem, llstream, scanner, idents, strutils, ast, msgs;
 
-function ParseFile(const filename: string): PNode;
+// function ParseFile(const filename: string): PNode;
 
 type
   TParser = record               // a TParser object represents a module that
@@ -31,10 +31,7 @@ type
     tok: PToken;                 // the current token
   end;
 
-function ParseModule(var p: TParser): PNode;
-// Note: The module's tree must always be valid.
-function parseExpr(var p: TParser): PNode;
-function parseStmt(var p: TParser): PNode;
+function ParseAll(var p: TParser): PNode;
 
 procedure openParser(var p: TParser; const filename: string;
                      inputstream: PLLStream);
@@ -44,21 +41,43 @@ function parseTopLevelStmt(var p: TParser): PNode;
 // implements an iterator. Returns the next top-level statement or nil if end
 // of stream.
 
-implementation
 
-function ParseFile(const filename: string): PNode;
-var
-  p: TParser;
-  f: TBinaryFile;
-begin
-  if not OpenFile(f, filename) then begin
-    rawMessage(errCannotOpenFile, filename);
-    exit
-  end;
-  OpenParser(p, filename, LLStreamOpen(f));
-  result := ParseModule(p);
-  CloseParser(p);
-end;
+// helpers for the other parsers
+function getPrecedence(tok: PToken): int;
+function isOperator(tok: PToken): bool;
+
+procedure getTok(var p: TParser);
+
+procedure parMessage(const p: TParser; const msg: TMsgKind;
+                     const arg: string = '');
+procedure skipComment(var p: TParser; node: PNode);
+
+function newNodeP(kind: TNodeKind; const p: TParser): PNode;
+function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
+                     const p: TParser): PNode;
+function newFloatNodeP(kind: TNodeKind; const floatVal: BiggestFloat;
+                       const p: TParser): PNode;
+function newStrNodeP(kind: TNodeKind; const strVal: string;
+                     const p: TParser): PNode;
+function newIdentNodeP(ident: PIdent; const p: TParser): PNode;
+
+procedure expectIdentOrKeyw(const p: TParser);
+procedure ExpectIdent(const p: TParser);
+procedure expectIdentOrOpr(const p: TParser);
+function parLineInfo(const p: TParser): TLineInfo;
+procedure Eat(var p: TParser; TokType: TTokType);
+
+procedure skipInd(var p: TParser);
+procedure optSad(var p: TParser);
+procedure optInd(var p: TParser; n: PNode);
+procedure indAndComment(var p: TParser; n: PNode);
+
+
+function parseSymbol(var p: TParser): PNode;
+function accExpr(var p: TParser): PNode;
+
+
+implementation
 
 procedure initParser(var p: TParser);
 begin
@@ -211,6 +230,9 @@ end;
 
 // ------------------- Expression parsing ------------------------------------
 
+function parseExpr(var p: TParser): PNode; forward;
+function parseStmt(var p: TParser): PNode; forward;
+
 function parseTypeDesc(var p: TParser): PNode; forward;
 function parseParamList(var p: TParser): PNode; forward;
 
@@ -364,43 +386,35 @@ begin
   addSon(result, optExpr(p));
 end;
 
-function parseTypeDescK(var p: TParser): PNode; forward;
-
-function namedTypeOrExpr(var p: TParser): PNode;
+function indexExpr(var p: TParser): PNode;
+// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
 var
   a, b: PNode;
 begin
-  case p.tok.tokType of
-    tkDotDot: result := dotdotExpr(p);
-    tkVar, tkRef, tkPtr, tkProc, tkTuple, tkType: result := parseTypeDescK(p);
-    else begin
-      a := parseExpr(p);
-      case p.tok.tokType of
-        tkEquals: begin
-          result := newNodeP(nkExprEqExpr, p);
-          addSon(result, a);
-          getTok(p);
-          //optInd(p, result);
-          case p.tok.tokType of
-            tkVar, tkRef, tkPtr, tkProc:
-              addSon(result, parseTypeDescK(p));
-            tkDotDot: addSon(result, dotdotExpr(p));
-            else begin
-              b := parseExpr(p);
-              if p.tok.tokType = tkDotDot then
-                b := dotdotExpr(p, b);
-              addSon(result, b);
-            end
-          end
-        end;
-        tkDotDot: result := dotdotExpr(p, a);
-        else result := a
-      end
+  if p.tok.tokType = tkDotDot then
+    result := dotdotExpr(p)
+  else begin
+    a := parseExpr(p);
+    case p.tok.tokType of
+      tkEquals: begin
+        result := newNodeP(nkExprEqExpr, p);
+        addSon(result, a);
+        getTok(p);
+        if p.tok.tokType = tkDotDot then
+          addSon(result, dotdotExpr(p))
+        else begin
+          b := parseExpr(p);
+          if p.tok.tokType = tkDotDot then b := dotdotExpr(p, b);
+          addSon(result, b);
+        end
+      end;
+      tkDotDot: result := dotdotExpr(p, a);
+      else result := a
     end
   end
 end;
 
-function namedTypeOrExprList(var p: TParser; first: PNode): PNode;
+function indexExprList(var p: TParser; first: PNode): PNode;
 var
   a: PNode;
 begin
@@ -410,7 +424,7 @@ begin
   optInd(p, result);
   while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
   and (p.tok.tokType <> tkSad) do begin
-    a := namedTypeOrExpr(p);
+    a := indexExpr(p);
     addSon(result, a);
     if p.tok.tokType <> tkComma then break;
     getTok(p);
@@ -464,7 +478,7 @@ begin
     getTok(p);
     optInd(p, result);
     a := result;
-    result := newNodeI(nkQualified, a.info);
+    result := newNodeI(nkDotExpr, a.info);
     addSon(result, a);
     addSon(result, parseSymbol(p));
   end;
@@ -632,7 +646,6 @@ begin
       result := newNodeP(nkNilLit, p);
       getTok(p);
     end;
-
     tkParLe: begin // () constructor
       result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi,
                                     tkColon);
@@ -698,7 +711,7 @@ begin
         addSon(result, a);
         getTok(p);
       end;
-      tkBracketLe: result := namedTypeOrExprList(p, result);
+      tkBracketLe: result := indexExprList(p, result);
       else break
     end
   end
@@ -712,11 +725,6 @@ var
 begin
   v := primary(p);
   // expand while operators have priorities higher than 'limit'
-  (*if p.tok.tokType = tkInd then begin // BUGFIX: parser allowed too much
-    getTok(p);
-    if getPrecedence(p.tok) < 0 then
-      parMessage(p, errOperatorExpected, tokToStr(p.tok));
-  end; *)
   op := p.tok;
   opPred := getPrecedence(p.tok);
   while (opPred > limit) do begin
@@ -724,8 +732,6 @@ begin
     opNode := newIdentNodeP(op.ident, p);
     // skip operator:
     getTok(p);
-    //skipComment(p, opNode);
-    //skipInd(p);
     optInd(p, opNode);
 
     // read sub-expression with higher priority
@@ -745,8 +751,6 @@ begin
 {@discard} lowestExprAux(p, result, -1);
 end;
 
-function parseLambda(var p: TParser): PNode; forward;
-
 function parseIfExpr(var p: TParser): PNode;
 var
   branch: PNode;
@@ -755,12 +759,9 @@ begin
   while true do begin
     getTok(p); // skip `if`, `elif`
     branch := newNodeP(nkElifExpr, p);
-    //optInd(p, branch);
     addSon(branch, parseExpr(p));
     eat(p, tkColon);
-    //optInd(p, branch);
     addSon(branch, parseExpr(p));
-    //optInd(p, branch);
     addSon(result, branch);
     if p.tok.tokType <> tkElif then break
   end;
@@ -770,16 +771,6 @@ begin
   addSon(result, branch);
 end;
 
-function parseExpr(var p: TParser): PNode;
-begin
-  case p.tok.toktype of
-    tkLambda: result := parseLambda(p);
-    tkIf:     result := parseIfExpr(p);
-    else      result := lowestExpr(p);
-  end
-end;
-
-// ------------------------- pragma parser -----------------------------------
 function parsePragma(var p: TParser): PNode;
 var
   a: PNode;
@@ -803,12 +794,203 @@ begin
     parMessage(p, errTokenExpected, '.}');
 end;
 
+function identVis(var p: TParser): PNode; // identifier with visability
+var
+  a: PNode;
+begin
+  a := parseSymbol(p);
+  if p.tok.tokType = tkOpr then begin
+    result := newNodeP(nkPostfix, p);
+    addSon(result, newIdentNodeP(p.tok.ident, p));
+    addSon(result, a);
+    getTok(p);
+  end
+  else
+    result := a;
+end;
+
+function identWithPragma(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  a := identVis(p);
+  if p.tok.tokType = tkCurlyDotLe then begin
+    result := newNodeP(nkPragmaExpr, p);
+    addSon(result, a);
+    addSon(result, parsePragma(p));
+  end
+  else
+    result := a
+end;
+
+type
+  TDeclaredIdentFlag = (
+    withPragma,                // identifier may have pragma
+    withBothOptional           // both ':' and '=' parts are optional
+  );
+  TDeclaredIdentFlags = set of TDeclaredIdentFlag;
+
+function parseIdentColonEquals(var p: TParser;
+                               flags: TDeclaredIdentFlags): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkIdentDefs, p);
+  while true do begin
+    case p.tok.tokType of
+      tkSymbol, tkAccent: begin
+        if withPragma in flags then
+          a := identWithPragma(p)
+        else
+          a := parseSymbol(p);
+        if a = nil then exit;
+      end;
+      else break;
+    end;
+    addSon(result, a);
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  if p.tok.tokType = tkColon then begin
+    getTok(p); optInd(p, result);
+    addSon(result, parseTypeDesc(p));
+  end
+  else begin
+    addSon(result, nil);
+    if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
+      parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
+  end;
+  if p.tok.tokType = tkEquals then begin
+    getTok(p); optInd(p, result);
+    addSon(result, parseExpr(p));
+  end
+  else
+    addSon(result, nil);
+end;
+
+function parseTuple(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkTupleTy, p);
+  getTok(p);
+  eat(p, tkBracketLe);
+  optInd(p, result);
+  while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
+    a := parseIdentColonEquals(p, {@set}[]);
+    addSon(result, a);        
+    if p.tok.tokType <> tkComma then break;
+    getTok(p);
+    optInd(p, a)
+  end;
+  optSad(p);
+  eat(p, tkBracketRi);
+end;
+
+function parseParamList(var p: TParser): PNode;
+var
+  a: PNode;
+begin
+  result := newNodeP(nkFormalParams, p);
+  addSon(result, nil); // return type
+  if p.tok.tokType = tkParLe then begin
+    getTok(p);
+    optInd(p, result);
+    while true do begin
+      case p.tok.tokType of
+        tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
+        tkParRi: break;
+        else begin parMessage(p, errTokenExpected, ')'+''); break; end;
+      end;
+      //optInd(p, a);
+      addSon(result, a);
+      if p.tok.tokType <> tkComma then break;
+      getTok(p);
+      optInd(p, a)
+    end;
+    optSad(p);
+    eat(p, tkParRi);
+  end;
+  if p.tok.tokType = tkColon then begin
+    getTok(p);
+    optInd(p, result);
+    result.sons[0] := parseTypeDesc(p)
+  end
+end;
+
+function parseProcExpr(var p: TParser; isExpr: bool): PNode;
+// either a proc type or a anonymous proc
+var
+  pragmas, params: PNode;
+  info: TLineInfo;
+begin
+  info := parLineInfo(p);
+  getTok(p);
+  params := parseParamList(p);
+  if p.tok.tokType = tkCurlyDotLe then pragmas := parsePragma(p)
+  else pragmas := nil;
+  if (p.tok.tokType = tkEquals) and isExpr then begin
+    result := newNodeI(nkLambda, info);
+    addSon(result, nil); // no name part
+    addSon(result, nil); // no generic parameters
+    addSon(result, params);
+    addSon(result, pragmas);
+    getTok(p); skipComment(p, result);
+    addSon(result, parseStmt(p));
+  end
+  else begin
+    result := newNodeI(nkProcTy, info);
+    addSon(result, params);
+    addSon(result, pragmas);
+  end
+end;
+
+function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
+begin
+  result := newNodeP(kind, p);
+  getTok(p);
+  optInd(p, result);
+  addSon(result, parseTypeDesc(p));
+end;
+
+function parseExpr(var p: TParser): PNode;
+(*
+expr ::= lowestExpr
+     | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
+     | 'var' expr
+     | 'ref' expr
+     | 'ptr' expr
+     | 'type' expr
+     | 'tuple' tupleDesc
+     | 'proc' paramList [pragma] ['=' stmt] 
+*)
+begin
+  case p.tok.toktype of
+    tkVar:   result := parseTypeDescKAux(p, nkVarTy);
+    tkRef:   result := parseTypeDescKAux(p, nkRefTy);
+    tkPtr:   result := parseTypeDescKAux(p, nkPtrTy);
+    tkType:  result := parseTypeDescKAux(p, nkTypeOfExpr);
+    tkTuple: result := parseTuple(p);
+    tkProc:  result := parseProcExpr(p, true);
+    tkIf:    result := parseIfExpr(p);
+    else     result := lowestExpr(p);
+  end
+end;
+
+function parseTypeDesc(var p: TParser): PNode;
+begin
+  if p.tok.toktype = tkProc then result := parseProcExpr(p, false)
+  else result := parseExpr(p);
+end;
+
 // ---------------------- statement parser ------------------------------------
 function isExprStart(const p: TParser): bool;
 begin
   case p.tok.tokType of
-    tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkLambda, tkBind,
-    tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit: result := true;
+    tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
+    tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit,
+    tkVar, tkRef, tkPtr, tkTuple, tkType: result := true;
     else result := false;
   end;
 end;
@@ -1051,7 +1233,6 @@ begin
     addSon(result, branch);
     if p.tok.tokType <> tkElif then break
   end;
-  //PrintTok(p.tok);
   if p.tok.tokType = tkElse then begin
     branch := newNodeP(nkElse, p);
     eat(p, tkElse); eat(p, tkColon);
@@ -1201,175 +1382,6 @@ begin
   getTok(p);
 end;
 
-function identVis(var p: TParser): PNode; // identifier with visability
-var
-  a: PNode;
-begin
-  a := parseSymbol(p);
-  if p.tok.tokType = tkOpr then begin
-    result := newNodeP(nkPostfix, p);
-    addSon(result, newIdentNodeP(p.tok.ident, p));
-    addSon(result, a);
-    getTok(p);
-  end
-  else
-    result := a;
-end;
-
-function identWithPragma(var p: TParser): PNode;
-var
-  a: PNode;
-begin
-  a := identVis(p);
-  if p.tok.tokType = tkCurlyDotLe then begin
-    result := newNodeP(nkPragmaExpr, p);
-    addSon(result, a);
-    addSon(result, parsePragma(p));
-  end
-  else
-    result := a
-end;
-
-type
-  TDeclaredIdentFlag = (
-    withPragma,                // identifier may have pragma
-    withBothOptional           // both ':' and '=' parts are optional
-  );
-  TDeclaredIdentFlags = set of TDeclaredIdentFlag;
-
-function parseIdentColonEquals(var p: TParser;
-                               flags: TDeclaredIdentFlags): PNode;
-var
-  a: PNode;
-begin
-  result := newNodeP(nkIdentDefs, p);
-  while true do begin
-    case p.tok.tokType of
-      tkSymbol, tkAccent: begin
-        if withPragma in flags then
-          a := identWithPragma(p)
-        else
-          a := parseSymbol(p);
-        if a = nil then exit;
-      end;
-      else break;
-    end;
-    addSon(result, a);
-    if p.tok.tokType <> tkComma then break;
-    getTok(p);
-    optInd(p, a)
-  end;
-  if p.tok.tokType = tkColon then begin
-    getTok(p); optInd(p, result);
-    addSon(result, parseTypeDesc(p));
-  end
-  else begin
-    addSon(result, nil);
-    if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
-      parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
-  end;
-  if p.tok.tokType = tkEquals then begin
-    getTok(p); optInd(p, result);
-    addSon(result, parseExpr(p));
-  end
-  else
-    addSon(result, nil);
-end;
-
-function parseParamList(var p: TParser): PNode;
-var
-  a: PNode;
-begin
-  result := newNodeP(nkFormalParams, p);
-  addSon(result, nil); // return type
-  if p.tok.tokType = tkParLe then begin
-    getTok(p);
-    optInd(p, result);
-    while true do begin
-      case p.tok.tokType of
-        tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
-        tkParRi: break;
-        else begin parMessage(p, errTokenExpected, ')'+''); break; end;
-      end;
-      //optInd(p, a);
-      addSon(result, a);
-      if p.tok.tokType <> tkComma then break;
-      getTok(p);
-      optInd(p, a)
-    end;
-    optSad(p);
-    eat(p, tkParRi);
-  end;
-  if p.tok.tokType = tkColon then begin
-    getTok(p);
-    optInd(p, result);
-    result.sons[0] := parseTypeDesc(p)
-  end
-end;
-
-function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
-begin
-  result := newNodeP(kind, p);
-  getTok(p);
-  optInd(p, result);
-  addSon(result, parseTypeDesc(p));
-end;
-
-function parseTypeDescK(var p: TParser): PNode;
-var
-  a: PNode;
-begin
-  case p.tok.tokType of
-    tkVar: result := parseTypeDescKAux(p, nkVarTy);
-    tkRef: result := parseTypeDescKAux(p, nkRefTy);
-    tkPtr: result := parseTypeDescKAux(p, nkPtrTy);
-    tkType: begin
-      result := newNodeP(nkTypeOfExpr, p);
-      getTok(p);
-      optInd(p, result);
-      addSon(result, parseExpr(p))
-    end;
-    tkProc: begin
-      result := newNodeP(nkProcTy, p);
-      getTok(p);
-      optInd(p, result);
-      addSon(result, parseParamList(p));
-      if p.tok.tokType = tkCurlyDotLe then
-        addSon(result, parsePragma(p))
-      else
-        addSon(result, nil);
-    end;
-    tkTuple: begin
-      result := newNodeP(nkTupleTy, p);
-      getTok(p);
-      eat(p, tkBracketLe);
-      optInd(p, result);
-      while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
-        a := parseIdentColonEquals(p, {@set}[]);
-        addSon(result, a);        
-        if p.tok.tokType <> tkComma then break;
-        getTok(p);
-        optInd(p, a)
-      end;
-      optSad(p);
-      eat(p, tkBracketRi);
-    end;
-    else begin
-      InternalError(parLineInfo(p), 'pnimsyn.parseTypeDescK');
-      result := nil
-    end
-  end
-end;
-
-function parseTypeDesc(var p: TParser): PNode;
-begin
-  case p.tok.tokType of
-    tkVar, tkRef, tkPtr, tkProc, tkType, tkTuple:
-      result := parseTypeDescK(p);
-    else result := primary(p)
-  end
-end;
-
 function parseGenericParamList(var p: TParser): PNode;
 var
   a: PNode;
@@ -1408,26 +1420,6 @@ begin
   indAndComment(p, result); // XXX: document this in the grammar!
 end;
 
-function parseLambda(var p: TParser): PNode;
-begin
-  result := newNodeP(nkLambda, p);
-  getTok(p);
-  optInd(p, result);
-  addSon(result, nil); // no name part
-  addSon(result, nil); // no generic parameters
-  addSon(result, parseParamList(p));
-  if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
-  else addSon(result, nil);
-  if p.tok.tokType = tkEquals then begin
-    getTok(p); skipComment(p, result);
-    addSon(result, parseStmt(p));
-  end
-  else begin
-    addSon(result, nil);
-    parMessage(p, errTokenExpected, tokTypeToStr[tkEquals]);
-  end
-end;
-
 function newCommentStmt(var p: TParser): PNode;
 begin
   result := newNodeP(nkCommentStmt, p);
@@ -1803,7 +1795,7 @@ begin
   end
 end;
 
-function parseModule(var p: TParser): PNode;
+function parseAll(var p: TParser): PNode;
 var
   a: PNode;
 begin
diff --git a/nim/pragmas.pas b/nim/pragmas.pas
index fc354cd2c..e8d07d52d 100755
--- a/nim/pragmas.pas
+++ b/nim/pragmas.pas
@@ -27,7 +27,7 @@ const
   procPragmas = {@set}[FirstCallConv..LastCallConv,
     wImportc, wExportc, wNodecl, wMagic, wNosideEffect, wSideEffect,
     wNoreturn, wDynLib, wHeader, wCompilerProc, wPure,
-    wCppMethod, wDeprecated, wVarargs, wCompileTime, wMerge,
+    wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
     wBorrow];
   converterPragmas = procPragmas;
   methodPragmas = procPragmas;
@@ -258,11 +258,13 @@ begin
   if (sym = nil) or (sym.kind = skModule) then
     POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic,
                                                       expectStrLit(c, n))
-  else begin
+  else if n.kind = nkExprColonExpr then begin
     lib := getLib(c, libDynamic, expectStrLit(c, n));
     addToLib(lib, sym);
     include(sym.loc.flags, lfDynamicLib)
   end
+  else 
+    include(sym.loc.flags, lfExportLib)
 end;
 
 procedure processNote(c: PContext; n: PNode);
@@ -475,12 +477,7 @@ begin
   if n = nil then exit;
   for i := 0 to sonsLen(n)-1 do begin
     it := n.sons[i];
-    if it.kind = nkExprColonExpr then begin
-      key := it.sons[0];
-    end
-    else begin
-      key := it;
-    end;
+    if it.kind = nkExprColonExpr then key := it.sons[0] else key := it;
     if key.kind = nkIdent then begin
       k := whichKeyword(key.ident);
       if k in validPragmas then begin
@@ -540,9 +537,9 @@ begin
             include(sym.flags, sfUsed); // suppress all those stupid warnings
             registerCompilerProc(sym);
           end;
-          wCppMethod: begin
-            makeExternImport(sym, getOptionalStr(c, it, sym.name.s));
-            include(sym.flags, sfCppMethod);
+          wProcvar: begin
+            noVal(it);
+            include(sym.flags, sfProcVar);
           end;
           wDeprecated: begin
             noVal(it);
@@ -615,6 +612,8 @@ begin
     end;
   end;
   if (sym <> nil) and (sym.kind <> skModule) then begin
+    if (lfExportLib in sym.loc.flags) and not (sfExportc in sym.flags) then
+      liMessage(n.info, errDynlibRequiresExportc);
     lib := POptionEntry(c.optionstack.tail).dynlib;
     if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and
          (sfImportc in sym.flags) and
diff --git a/nim/ptmplsyn.pas b/nim/ptmplsyn.pas
index 2368b22c7..717da6ee0 100755
--- a/nim/ptmplsyn.pas
+++ b/nim/ptmplsyn.pas
@@ -1,28 +1,31 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
 //
 unit ptmplsyn;
 
-// This module implements the parser of the Nimrod Template files.
+// This module implements Nimrod's standard template filter.
 
 {$include config.inc}
 
 interface
 
 uses
-  nsystem, llstream, nos, charsets, wordrecg, strutils,
-  ast, astalgo, msgs, options, pnimsyn;
+  nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
+  ast, astalgo, msgs, options, rnimsyn, filters;
 
-function ParseTmplFile(const filename: string): PNode;
+function filterTmpl(input: PLLStream; const filename: string;
+                    call: PNode): PLLStream;
+// #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
 
+implementation
 
 type
-  TParseState = (psDirective, psMultiDir, psTempl);
+  TParseState = (psDirective, psTempl);
   TTmplParser = record
     inp: PLLStream;
     state: TParseState;
@@ -30,19 +33,11 @@ type
     indent, par: int;
     x: string; // the current input line
     outp: PLLStream; // the ouput will be parsed by pnimsyn
-    subsChar: Char;
+    subsChar, NimDirective: Char;
+    emit, conc, toStr: string;
   end;
 
-function ParseTmpl(var p: TTmplParser): PNode;
-
-procedure openTmplParser(var p: TTmplParser; const filename: string;
-                         inputStream: PLLStream);
-procedure closeTmplParser(var p: TTmplParser);
-
-implementation
-
 const
-  NimDirective = '#';
   PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255, '.', '_'];
 
 procedure newLine(var p: TTmplParser);
@@ -59,73 +54,45 @@ var
 begin
   j := strStart;
   while p.x[j] = ' ' do inc(j);
-  if p.state = psMultiDir then begin
-    newLine(p);
-    if p.x[j] = '*' then begin
-      inc(j);
-      if p.x[j] = NimDirective then p.state := psTempl;
-      // ignore the rest of the line
-    end
-    else
-      LLStreamWrite(p.outp, p.x); // simply add the whole line
-  end
-  else if p.x[j] = NimDirective then begin
+  if (p.x[strStart] = p.NimDirective) and (p.x[strStart+1] = '!') then
+    newLine(p)
+  else if (p.x[j] = p.NimDirective) then begin
     newLine(p);
     inc(j);
     while p.x[j] = ' ' do inc(j);
     d := j;
-    if p.x[j] = '*' then begin
+    keyw := '';
+    while p.x[j] in PatternChars do begin
+      addChar(keyw, p.x[j]);
       inc(j);
-      p.state := psMultiDir;
-      LLStreamWrite(p.outp, repeatChar(p.indent));
-      LLStreamWrite(p.outp, '#*');
-      LLStreamWrite(p.outp, ncopy(p.x, j)); // simply add the whole line
-    end
-    else begin
-      keyw := '';
-      while p.x[j] in PatternChars do begin
-        addChar(keyw, p.x[j]);
-        inc(j);
-      end;
-      case whichKeyword(keyw) of
-        wEnd: begin
-          if p.indent >= 2 then
-            dec(p.indent, 2)
-          else begin
-            p.info.col := int16(j);
-            liMessage(p.info, errXNotAllowedHere, 'end');
-          end;
-          LLStreamWrite(p.outp, repeatChar(p.indent));
-          LLStreamWrite(p.outp, '#end');
-        end;
-        wSubsChar: begin
-          LLStreamWrite(p.outp, repeatChar(p.indent));
-          LLStreamWrite(p.outp, '#subschar');
-          while p.x[j] = ' ' do inc(j);
-          if p.x[j] in ['+', '-', '*', '/', '<', '>', '!', '?', '^', '.',
-                 '|', '=', '%', '&', '$', '@', '~'] then p.subsChar := p.x[j]
-          else begin
-            p.info.col := int16(j);
-            liMessage(p.info, errXNotAllowedHere, p.x[j]+'');
-          end
-        end;
-        wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
-        wConverter, wMacro, wTemplate: begin
-          LLStreamWrite(p.outp, repeatChar(p.indent));
-          LLStreamWrite(p.outp, ncopy(p.x, d));
-          inc(p.indent, 2);
-        end;
-        wElif, wOf, wElse, wExcept, wFinally: begin
-          LLStreamWrite(p.outp, repeatChar(p.indent-2));
-          LLStreamWrite(p.outp, ncopy(p.x, d));
-        end
+    end;
+    case whichKeyword(keyw) of
+      wEnd: begin
+        if p.indent >= 2 then
+          dec(p.indent, 2)
         else begin
-          LLStreamWrite(p.outp, repeatChar(p.indent));
-          LLStreamWrite(p.outp, ncopy(p.x, d));
-        end
+          p.info.col := int16(j);
+          liMessage(p.info, errXNotAllowedHere, 'end');
+        end;
+        LLStreamWrite(p.outp, repeatChar(p.indent));
+        LLStreamWrite(p.outp, '#end');
+      end;
+      wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
+      wConverter, wMacro, wTemplate, wMethod: begin
+        LLStreamWrite(p.outp, repeatChar(p.indent));
+        LLStreamWrite(p.outp, ncopy(p.x, d));
+        inc(p.indent, 2);
       end;
-      p.state := psDirective;
-    end
+      wElif, wOf, wElse, wExcept, wFinally: begin
+        LLStreamWrite(p.outp, repeatChar(p.indent-2));
+        LLStreamWrite(p.outp, ncopy(p.x, d));
+      end
+      else begin
+        LLStreamWrite(p.outp, repeatChar(p.indent));
+        LLStreamWrite(p.outp, ncopy(p.x, d));
+      end
+    end;
+    p.state := psDirective
   end
   else begin
     // data line
@@ -133,17 +100,18 @@ begin
     case p.state of
       psTempl: begin
         // next line of string literal:
-        LLStreamWrite(p.outp, ' &'+nl);
+        LLStreamWrite(p.outp, p.conc);
+        LLStreamWrite(p.outp, nl);
         LLStreamWrite(p.outp, repeatChar(p.indent + 2));
         LLStreamWrite(p.outp, '"'+'');
       end;
       psDirective: begin
         newLine(p);
         LLStreamWrite(p.outp, repeatChar(p.indent));
-        LLStreamWrite(p.outp, 'add(result, "');
+        LLStreamWrite(p.outp, p.emit);
+        LLStreamWrite(p.outp, '("');
         inc(p.par);
-      end;
-      else InternalError(p.info, 'parser in invalid state');
+      end
     end;
     p.state := psTempl;
     while true do begin
@@ -162,7 +130,10 @@ begin
           case p.x[j] of
             '{': begin
               p.info.col := int16(j);
-              LLStreamWrite(p.outp, '" & $(');
+              LLStreamWrite(p.outp, '"');
+              LLStreamWrite(p.outp, p.conc);
+              LLStreamWrite(p.outp, p.toStr);
+              LLStreamWrite(p.outp, '(');
               inc(j);
               curly := 0;
               while true do begin
@@ -171,13 +142,13 @@ begin
                   '{': begin
                     inc(j);
                     inc(curly);
-                    LLStreamWrite(p.outp, '{'+'');
+                    LLStreamWrite(p.outp, '{');
                   end;
                   '}': begin
                     inc(j);
                     if curly = 0 then break;
                     if curly > 0 then dec(curly);
-                    LLStreamWrite(p.outp, '}'+'');
+                    LLStreamWrite(p.outp, '}');
                   end;
                   else begin
                     LLStreamWrite(p.outp, p.x[j]);
@@ -185,15 +156,22 @@ begin
                   end
                 end
               end;
-              LLStreamWrite(p.outp, ') & "')
+              LLStreamWrite(p.outp, ')');
+              LLStreamWrite(p.outp, p.conc);
+              LLStreamWrite(p.outp, '"');
             end;
-            'A'..'Z', 'a'..'z', '_': begin
-              LLStreamWrite(p.outp, '" & $');
+            'a'..'z', 'A'..'Z', #128..#255: begin
+              LLStreamWrite(p.outp, '"');
+              LLStreamWrite(p.outp, p.conc);
+              LLStreamWrite(p.outp, p.toStr);
+              LLStreamWrite(p.outp, '(');
               while p.x[j] in PatternChars do begin
                 LLStreamWrite(p.outp, p.x[j]);
                 inc(j)
               end;
-              LLStreamWrite(p.outp, ' & "')
+              LLStreamWrite(p.outp, ')');
+              LLStreamWrite(p.outp, p.conc);
+              LLStreamWrite(p.outp, '"')
             end;
             else if p.x[j] = p.subsChar then begin
               LLStreamWrite(p.outp, p.subsChar);
@@ -203,7 +181,7 @@ begin
               p.info.col := int16(j);
               liMessage(p.info, errInvalidExpression, '$'+'');
             end
-          end;
+          end
         end
         else begin
           LLStreamWrite(p.outp, p.x[j]);
@@ -215,56 +193,30 @@ begin
   end
 end;
 
-function ParseTmpl(var p: TTmplParser): PNode;
+function filterTmpl(input: PLLStream; const filename: string;
+                    call: PNode): PLLStream;
 var
-  q: TParser;
+  p: TTmplParser; 
 begin
+{@ignore}
+  FillChar(p, sizeof(p), 0);
+{@emit}
+  p.info := newLineInfo(filename, 0, 0);
+  p.outp := LLStreamOpen('');
+  p.inp := input;
+  p.subsChar := charArg(call, 'subschar', 1, '$');
+  p.nimDirective := charArg(call, 'metachar', 2, '#');
+  p.emit := strArg(call, 'emit', 3, 'result.add');
+  p.conc := strArg(call, 'conc', 4, ' & ');
+  p.toStr := strArg(call, 'tostring', 5, '$'+'');
   while not LLStreamAtEnd(p.inp) do begin
     p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit};
     p.info.line := p.info.line + int16(1);
     parseLine(p);
   end;
   newLine(p);
-  if gVerbosity >= 2 then begin
-    rawMessage(hintCodeBegin);
-    messageOut(p.outp.s);
-    rawMessage(hintCodeEnd);
-  end;
-  openParser(q, toFilename(p.info), p.outp);
-  result := ParseModule(q);
-  closeParser(q);
-end;
-
-procedure openTmplParser(var p: TTmplParser; const filename: string;
-                         inputStream: PLLStream);
-begin
-{@ignore}
-  FillChar(p, sizeof(p), 0);
-{@emit}
-  p.info := newLineInfo(filename, 0, 0);
-  p.outp := LLStreamOpen('');
-  p.inp := inputStream;
-  p.subsChar := '$';
-end;
-
-procedure CloseTmplParser(var p: TTmplParser);
-begin
+  result := p.outp;
   LLStreamClose(p.inp);
 end;
 
-function ParseTmplFile(const filename: string): PNode;
-var
-  p: TTmplParser;
-  f: TBinaryFile;
-begin
-  if not OpenFile(f, filename) then begin
-    rawMessage(errCannotOpenFile, filename);
-    result := nil;
-    exit
-  end;
-  OpenTmplParser(p, filename, LLStreamOpen(f));
-  result := ParseTmpl(p);
-  CloseTmplParser(p);
-end;
-
 end.
diff --git a/nim/rnimsyn.pas b/nim/rnimsyn.pas
index 1ad1c9408..ec1e9571e 100755
--- a/nim/rnimsyn.pas
+++ b/nim/rnimsyn.pas
@@ -498,7 +498,7 @@ begin
     nkPar, nkCurly, nkBracket: result := lcomma(n)+2;
     nkSymChoice: result := lsons(n) + length('()') + sonsLen(n)-1;
     nkTupleTy: result := lcomma(n)+length('tuple[]');
-    nkQualified, nkDotExpr: result := lsons(n)+1;
+    nkDotExpr: result := lsons(n)+1;
     nkBind: result := lsons(n)+length('bind_');
     nkCheckedFieldExpr: result := lsub(n.sons[0]);
     nkLambda: result := lsons(n)+length('lambda__=_');
@@ -1047,7 +1047,7 @@ begin
       gcomma(g, n, c);
       put(g, tkBracketRi, ']'+'');
     end;
-    nkQualified, nkDotExpr: begin
+    nkDotExpr: begin
       gsub(g, n.sons[0]);
       put(g, tkDot, '.'+'');
       gsub(g, n.sons[1]);
diff --git a/nim/rodread.pas b/nim/rodread.pas
index dc3630a45..457ad6cc2 100755
--- a/nim/rodread.pas
+++ b/nim/rodread.pas
@@ -1057,8 +1057,7 @@ begin
         // NOTE: we need to process the entire module graph so that no ID will
         // be used twice! However, compilation speed does not suffer much from
         // this, since results are cached.
-        res := checkDep(JoinPath(options.libpath, 
-                        appendFileExt('system', nimExt)));
+        res := checkDep(JoinPath(options.libpath, addFileExt('system', nimExt)));
         if res <> rrNone then result := rrModDeps;
         for i := 0 to high(r.modDeps) do begin
           res := checkDep(r.modDeps[i]);
diff --git a/nim/rst.pas b/nim/rst.pas
index d5cde5c7e..fb29b7d9f 100755
--- a/nim/rst.pas
+++ b/nim/rst.pas
@@ -314,6 +314,9 @@ begin
   len := length(tokens);
   L.buf := PChar(buffer);
   L.line := 1;
+  // skip UTF-8 BOM
+  if (L.buf[0] = #239) and (L.buf[1] = #187) and (L.buf[2] = #191) then 
+    inc(L.bufpos, 3);
   L.skipPounds := skipPounds;
   if skipPounds then begin
     if L.buf[L.bufpos] = '#' then inc(L.bufpos);
diff --git a/nim/scanner.pas b/nim/scanner.pas
index 05f903622..c03ae9224 100755
--- a/nim/scanner.pas
+++ b/nim/scanner.pas
@@ -143,19 +143,16 @@ type
                            // or float literals
     literal: string;       // the parsed (string) literal; and
                            // documentation comments are here too
-    next: PToken;          // next token; used for arbitrary look-ahead
+    next: PToken;          // next token; can be used for arbitrary look-ahead
   end;
 
   PLexer = ^TLexer;
   TLexer = object(TBaseLexer)
-    // lexers can be put into a stack through the next pointer;
-    // this feature is currently unused, however
     filename: string;
-    next: PLexer;
     indentStack: array of int; // the indentation stack
     dedent: int;             // counter for DED token generation
     indentAhead: int;        // if > 0 an indendation has already been read
-                             // this is needed because scanning # comments
+                             // this is needed because scanning comments
                              // needs so much look-ahead
   end;
 
@@ -920,7 +917,6 @@ begin
   // got an documentation comment or tkIndent, return that:
   if tok.toktype <> tkInvalid then exit;
 
-  // to the parser
   c := L.buf[L.bufpos];
   if c in SymStartChars - ['r', 'R', 'l'] then // common case first
     getSymbol(L, tok)
diff --git a/nim/semexprs.pas b/nim/semexprs.pas
index 43eb9ac6f..91c909dfd 100755
--- a/nim/semexprs.pas
+++ b/nim/semexprs.pas
@@ -374,15 +374,34 @@ begin
   end
 end;
 
-function isAssignable(n: PNode): bool;
+type
+  TAssignableResult = (
+    arNone,              // no l-value and no discriminant
+    arLValue,            // is an l-value
+    arDiscriminant       // is a discriminant
+  );
+
+function isAssignable(n: PNode): TAssignableResult;
 begin
-  result := false;
+  result := arNone;
   case n.kind of
-    nkSym: result := (n.sym.kind in [skVar, skTemp]);
-    nkDotExpr, nkQualified, nkBracketExpr: begin
+    nkSym: begin
+      if (n.sym.kind in [skVar, skTemp]) then
+        result := arLValue
+    end;
+    nkDotExpr: begin
+      checkMinSonsLen(n, 1);
+      if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
+        result := arLValue
+      else
+        result := isAssignable(n.sons[0]);
+      if (result = arLValue) and (sfDiscriminant in n.sons[1].sym.flags) then
+        result := arDiscriminant
+    end;
+    nkBracketExpr: begin
       checkMinSonsLen(n, 1);
       if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
-        result := true
+        result := arLValue
       else
         result := isAssignable(n.sons[0]);
     end;
@@ -393,7 +412,7 @@ begin
       if skipTypes(n.typ, abstractPtrs).kind in [tyOpenArray, tyTuple, tyObject] then
         result := isAssignable(n.sons[1])
     end;
-    nkHiddenDeref, nkDerefExpr: result := true;
+    nkHiddenDeref, nkDerefExpr: result := arLValue;
     nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
       result := isAssignable(n.sons[0]);
     else begin end
@@ -409,7 +428,7 @@ begin
   else begin
     result := newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ));
     addSon(result, n);
-    if not isAssignable(n) then begin
+    if isAssignable(n) <> arLValue then begin
       liMessage(n.info, errVarForOutParamNeeded);
     end
   end
@@ -425,7 +444,7 @@ begin
         result := newHiddenAddrTaken(c, n);
       end
     end;
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       checkSonsLen(n, 2);
       if n.sons[1].kind <> nkSym then
         internalError(n.info, 'analyseIfAddressTaken');
@@ -499,7 +518,7 @@ begin
   prc := n.sons[0];
   checkMinSonsLen(n, 1);
   case n.sons[0].kind of
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       checkSonsLen(n.sons[0], 2);
       n.sons[0] := semDotExpr(c, n.sons[0]);
       if n.sons[0].kind = nkDotCall then begin // it is a static call!
@@ -595,7 +614,7 @@ begin
       else
         result := SymtabGet(c.Tab, n.ident); // no need for stub loading
     end;
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       result := nil;
       if onlyCurrentScope then exit;
       checkSonsLen(n, 2);
@@ -1174,8 +1193,14 @@ begin
     liMessage(n.info, errATypeHasNoValue);
   case s.kind of
     skProc, skMethod, skIterator, skConverter: begin
-      if (s.magic <> mNone) then
-        liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
+      if not (sfProcVar in s.flags) 
+      and (s.typ.callConv = ccDefault)
+      and (getModule(s).id <> c.module.id) then
+        liMessage(n.info, warnXisPassedToProcVar, s.name.s);
+      // XXX change this to errXCannotBePassedToProcVar after version 0.8.2
+      // TODO VERSION 0.8.4
+      //if (s.magic <> mNone) then
+      //  liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
       result := symChoice(c, n, s);
     end;
     skConst: begin
@@ -1253,10 +1278,7 @@ begin
       (*s := n.sym;
       include(s.flags, sfUsed);
       if (s.kind = skType) and not (efAllowType in flags) then
-        liMessage(n.info, errATypeHasNoValue);
-      if (s.magic <> mNone) and
-          (s.kind in [skProc, skMethod, skIterator, skConverter]) then
-        liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *)
+        liMessage(n.info, errATypeHasNoValue);*)
       // because of the changed symbol binding, this does not mean that we
       // don't have to check the symbol for semantics here again!
       result := semSym(c, n, n.sym, flags);
@@ -1279,7 +1301,7 @@ begin
       if result.typ = nil then result.typ := getSysType(tyString);
     nkCharLit:
       if result.typ = nil then result.typ := getSysType(tyChar);
-    nkQualified, nkDotExpr: begin
+    nkDotExpr: begin
       result := semDotExpr(c, n, flags);
       if result.kind = nkDotCall then begin
         result.kind := nkCall;
@@ -1364,7 +1386,8 @@ begin
       result := n;
       checkSonsLen(n, 1);
       n.sons[0] := semExprWithType(c, n.sons[0]);
-      if not isAssignable(n.sons[0]) then liMessage(n.info, errExprHasNoAddress);
+      if isAssignable(n.sons[0]) <> arLValue then 
+        liMessage(n.info, errExprHasNoAddress);
       n.typ := makePtrType(c, n.sons[0].typ);
     end;
     nkHiddenAddr, nkHiddenDeref: begin
diff --git a/nim/semstmts.pas b/nim/semstmts.pas
index b4b72a25b..1ece72023 100755
--- a/nim/semstmts.pas
+++ b/nim/semstmts.pas
@@ -127,7 +127,7 @@ var
   str, sub: string;
   a, b, c: int;
   e: PSym;
-  marker: Char;
+  marker: char;
 begin
   result := n;
   checkSonsLen(n, 2);
@@ -243,7 +243,7 @@ begin
   checkSonsLen(n, 2);
   a := n.sons[0];
   case a.kind of
-    nkDotExpr, nkQualified: begin
+    nkDotExpr: begin
       // r.f = x
       // --> `f=` (r, x)
       checkSonsLen(a, 2);
@@ -298,7 +298,8 @@ begin
   n.sons[1] := semExprWithType(c, n.sons[1]);
   le := n.sons[0].typ;
   if (skipTypes(le, {@set}[tyGenericInst]).kind <> tyVar) 
-  and not IsAssignable(n.sons[0]) then begin
+  and (IsAssignable(n.sons[0]) = arNone) then begin
+    // Direct assignment to a discriminant is allowed!
     liMessage(n.sons[0].info, errXCannotBeAssignedTo,
               renderTree(n.sons[0], {@set}[renderNoComments]));
   end
@@ -1014,8 +1015,7 @@ end;
 function evalInclude(c: PContext; n: PNode): PNode;
 var
   i, fileIndex: int;
-  x: PNode;
-  f, name, ext: string;
+  f: string;
 begin
   result := newNodeI(nkStmtList, n.info);
   addSon(result, n); // the rodwriter needs include information!
@@ -1024,13 +1024,7 @@ begin
     fileIndex := includeFilename(f);
     if IntSetContainsOrIncl(c.includedFiles, fileIndex) then
       liMessage(n.info, errRecursiveDependencyX, f);
-    SplitFilename(f, name, ext);
-    if cmpIgnoreCase(ext, '.'+TmplExt) = 0 then
-      x := gIncludeTmplFile(f)
-    else
-      x := gIncludeFile(f);
-    x := semStmt(c, x);
-    addSon(result, x);
+    addSon(result, semStmt(c, gIncludeFile(f)));
     IntSetExcl(c.includedFiles, fileIndex);
   end;
 end;
diff --git a/nim/semtempl.pas b/nim/semtempl.pas
index 8e37a1ea3..e639960d3 100755
--- a/nim/semtempl.pas
+++ b/nim/semtempl.pas
@@ -34,7 +34,7 @@ begin
   if n = nil then begin result := false; exit end;
   case n.kind of
     nkIdent, nkSym, nkType: result := true;
-    nkDotExpr, nkQualified, nkBracketExpr: begin
+    nkDotExpr, nkBracketExpr: begin
       for i := 0 to sonsLen(n)-1 do
         if not isTypeDesc(n.sons[i]) then begin
           result := false; exit
diff --git a/nim/semtypes.pas b/nim/semtypes.pas
index c0b86be86..e2a0d2185 100755
--- a/nim/semtypes.pas
+++ b/nim/semtypes.pas
@@ -758,6 +758,10 @@ begin
     nkTypeOfExpr: begin
       result := semExprWithType(c, n, {@set}[efAllowType]).typ;
     end;
+    nkPar: begin
+      if sonsLen(n) = 1 then result := semTypeNode(c, n.sons[0], prev)
+      else liMessage(n.info, errTypeExpected);
+    end;
     nkBracketExpr: begin
       checkMinSonsLen(n, 2);
       s := semTypeIdent(c, n.sons[0]);
@@ -771,7 +775,7 @@ begin
         else result := semGeneric(c, n, s, prev);
       end
     end;
-    nkIdent, nkDotExpr, nkQualified, nkAccQuoted: begin
+    nkIdent, nkDotExpr, nkAccQuoted: begin
       s := semTypeIdent(c, n);
       if s.typ = nil then
         liMessage(n.info, errTypeExpected);
diff --git a/nim/strutils.pas b/nim/strutils.pas
index be20b52b2..afd8b4766 100755
--- a/nim/strutils.pas
+++ b/nim/strutils.pas
@@ -50,7 +50,7 @@ function ToString(b: PChar): string; overload;
 function IntToStr(i: BiggestInt; minChars: int): string;
 
 function find(const s, sub: string; start: int = 1): int; overload;
-function replaceStr(const s, search, by: string): string;
+function replace(const s, search, by: string): string;
 procedure deleteStr(var s: string; first, last: int);
 
 function ToLower(const s: string): string;
@@ -63,9 +63,6 @@ function ParseFloat(const s: string; checkEnd: Boolean = True): Real;
 
 function repeatChar(count: int; c: Char = ' '): string;
 
-type
-  TStringSeq = array of string;
-  TCharSet = set of Char;
 function split(const s: string; const seps: TCharSet): TStringSeq;
 
 function startsWith(const s, prefix: string): bool;
@@ -278,7 +275,7 @@ begin
   end
 end;
 
-function replaceStr(const s, search, by: string): string;
+function replace(const s, search, by: string): string;
 var
   i, j: int;
 begin
diff --git a/nim/syntaxes.pas b/nim/syntaxes.pas
index 1068064ce..19035028e 100755
--- a/nim/syntaxes.pas
+++ b/nim/syntaxes.pas
@@ -1,64 +1,234 @@
 //
 //
 //           The Nimrod Compiler
-//        (c) Copyright 2008 Andreas Rumpf
+//        (c) Copyright 2009 Andreas Rumpf
 //
 //    See the file "copying.txt", included in this
 //    distribution, for details about the copyright.
 //
 unit syntaxes;
 
-// Defines the common interface of all parsers & renderers.
-// All parsers and renderers need to register here!
-// This file is currently unused.
+// Implements the dispatcher for the different parsers.
 {$include 'config.inc'}
 
 interface
 
 uses
-  nsystem, strutils, ast, scanner, pnimsyn, rnimsyn, options, msgs,
-  nos, lists, condsyms, paslex, pasparse, rodgen, ropes, trees;
-
-// how to handle the different keyword sets?
-// PIdent does not support multiple ids! But I want to allow
-// constant expressions, else the case-statement wouldn't work
-// resulting in ugly code -> let the build system deal with it!
-// IDEA: the scanner changes the IDs for its keywords: Won't work!
-// How to deal with the `` operator?
+  nsystem, strutils, llstream, ast, astalgo, idents, scanner, options, msgs, 
+  pnimsyn, ptmplsyn, filters, rnimsyn;
 
 type
-  TSyntaxes = (synStandard, synCurly, synLisp);
+  TFilterKind = (filtNone, filtTemplate, filtReplace, filtStrip);
+  TParserKind = (skinStandard, skinBraces, skinEndX);
+  
+const
+  parserNames: array [TParserKind] of string = ('standard', 'braces', 'endx');
+  filterNames: array [TFilterKind] of string = ('none', 'stdtmpl', 'replace',
+                                                'strip');
+  
+type
+  TParsers = record
+    skin: TParserKind;
+    parser: TParser;
+  end;
+
+{@ignore} 
+function ParseFile(const filename: string): PNode;
+{@emit
+function ParseFile(const filename: string): PNode; procvar;
+}
+
+procedure openParsers(var p: TParsers; const filename: string;
+                     inputstream: PLLStream);
+procedure closeParsers(var p: TParsers);
+function parseAll(var p: TParsers): PNode;
+
+function parseTopLevelStmt(var p: TParsers): PNode;
+// implements an iterator. Returns the next top-level statement or nil if end
+// of stream.
 
-function parseFile(const filename: string): PNode;
 
 implementation
 
-type
-  TFileParser = function (const filename: string): PNode;
-  TBufferParser = function (const buf, filename: string; 
-                            line, column: int): PNode;
-  TRenderer = function (n: PNode): string;
-  THeadParser = function (const line: string): bool;
-  TSyntax = record
-    name: string;            // name of the syntax
-    headParser: THeadParser; // the head parser
-    parser: TParser;         // the parser for the syntax
-    renderer: TRenderer;     // renderer of the syntax; may be nil
+function ParseFile(const filename: string): PNode;
+var
+  p: TParsers;
+  f: TBinaryFile;
+begin
+  if not OpenFile(f, filename) then begin
+    rawMessage(errCannotOpenFile, filename);
+    exit
   end;
+  OpenParsers(p, filename, LLStreamOpen(f));
+  result := ParseAll(p);
+  CloseParsers(p);
+end;
 
+function parseAll(var p: TParsers): PNode;
+begin
+  case p.skin of
+    skinStandard: result := pnimsyn.parseAll(p.parser);
+    skinBraces, skinEndX: InternalError('parser to implement');
+    // skinBraces: result := pbraces.parseAll(p.parser);
+    // skinEndX: result := pendx.parseAll(p.parser);
+  end
+end;
+
+function parseTopLevelStmt(var p: TParsers): PNode;
+begin
+  case p.skin of
+    skinStandard: result := pnimsyn.parseTopLevelStmt(p.parser);
+    skinBraces, skinEndX: InternalError('parser to implement');
+    //skinBraces: result := pbraces.parseTopLevelStmt(p.parser);
+    //skinEndX: result := pendx.parseTopLevelStmt(p.parser);
+  end
+end;
+
+function UTF8_BOM(const s: string): int;
+begin
+  if (s[strStart] = #239) and (s[strStart+1] = #187) 
+  and (s[strStart+2] = #191) then result := 3
+  else result := 0
+end;
+
+function containsShebang(const s: string; i: int): bool;
 var
-  syntaxes: array [TSyntaxes] of TSyntax;
+  j: int;
+begin
+  result := false;
+  if (s[i] = '#') and (s[i+1] = '!') then begin
+    j := i+2;
+    while s[j] in WhiteSpace do inc(j);
+    result := s[j] = '/'
+  end
+end;
 
-procedure addSyntax(const s: TSyntax);
+function parsePipe(const filename: string; inputStream: PLLStream): PNode;
 var
-  len: int;
+  line: string;
+  s: PLLStream;
+  i: int;
+  q: TParser;
 begin
-  len := length(syntaxes);
-  setLength(syntaxes, len+1);
-  syntaxes[len] := s;
+  result := nil;
+  s := LLStreamOpen(filename, fmRead);
+  if s <> nil then begin
+    line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
+    i := UTF8_Bom(line) + strStart;
+    if containsShebang(line, i) then begin
+      line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
+      i := strStart;
+    end;
+    if (line[i] = '#') and (line[i+1] = '!') then begin
+      inc(i, 2);
+      while line[i] in WhiteSpace do inc(i);
+      OpenParser(q, filename, LLStreamOpen(ncopy(line, i)));
+      result := pnimsyn.parseAll(q);
+      CloseParser(q);
+    end;
+    LLStreamClose(s);
+  end
+end;
+
+function getFilter(ident: PIdent): TFilterKind;
+var
+  i: TFilterKind;
+begin
+  for i := low(TFilterKind) to high(TFilterKind) do
+    if IdentEq(ident, filterNames[i]) then begin
+      result := i; exit
+    end;
+  result := filtNone
+end;
+
+function getParser(ident: PIdent): TParserKind;
+var
+  i: TParserKind;
+begin
+  for i := low(TParserKind) to high(TParserKind) do
+    if IdentEq(ident, parserNames[i]) then begin
+      result := i; exit
+    end;
+  rawMessage(errInvalidDirectiveX, ident.s);
+end;
+
+function getCallee(n: PNode): PIdent;
+begin
+  if (n.kind = nkCall) and (n.sons[0].kind = nkIdent) then
+    result := n.sons[0].ident
+  else if n.kind = nkIdent then result := n.ident
+  else rawMessage(errXNotAllowedHere, renderTree(n));
+end;
+
+function applyFilter(var p: TParsers; n: PNode; const filename: string; 
+                     input: PLLStream): PLLStream;
+var
+  ident: PIdent;
+  f: TFilterKind;
+begin
+  ident := getCallee(n);
+  f := getFilter(ident); 
+  case f of
+    filtNone: begin
+      p.skin := getParser(ident);
+      result := input
+    end;
+    filtTemplate: result := filterTmpl(input, filename, n);
+    filtStrip: result := filterStrip(input, filename, n);
+    filtReplace: result := filterReplace(input, filename, n);
+  end;
+  if f <> filtNone then begin
+    if gVerbosity >= 2 then begin
+      rawMessage(hintCodeBegin);
+      messageOut(result.s);
+      rawMessage(hintCodeEnd);
+    end
+  end
+end;
+
+function evalPipe(var p: TParsers; n: PNode; const filename: string;
+                  start: PLLStream): PLLStream;
+var 
+  i: int;
+begin
+  result := start;
+  if n = nil then exit;
+  if (n.kind = nkInfix) and (n.sons[0].kind = nkIdent)
+  and IdentEq(n.sons[0].ident, '|'+'') then begin
+    for i := 1 to 2 do begin
+      if n.sons[i].kind = nkInfix then
+        result := evalPipe(p, n.sons[i], filename, result)
+      else
+        result := applyFilter(p, n.sons[i], filename, result)
+    end
+  end
+  else if n.kind = nkStmtList then 
+    result := evalPipe(p, n.sons[0], filename, result)
+  else
+    result := applyFilter(p, n, filename, result)
+end;
+
+procedure openParsers(var p: TParsers; const filename: string;
+                     inputstream: PLLStream);
+var
+  pipe: PNode;
+  s: PLLStream;
+begin
+  p.skin := skinStandard;
+  pipe := parsePipe(filename, inputStream);
+  if pipe <> nil then 
+    s := evalPipe(p, pipe, filename, inputStream)
+  else
+    s := inputStream;
+  case p.skin of
+    skinStandard, skinBraces, skinEndX:
+      pnimsyn.openParser(p.parser, filename, s);
+  end
+end;
+
+procedure closeParsers(var p: TParsers);
+begin
+  pnimsyn.closeParser(p.parser);
 end;
 
-initialization
-  syntaxes[synStandard].name = 'Standard';
-  
 end.
diff --git a/nim/transf.pas b/nim/transf.pas
index d5b0bb6f8..192d76a84 100755
--- a/nim/transf.pas
+++ b/nim/transf.pas
@@ -652,7 +652,7 @@ end;
       f: proc (x: int, c: PClosure): int
 
   proc map(f: PClosure, a: seq[int]): seq[int] =
-    result = []
+    result = @[]
     for elem in a:
       add result, f.f(a, f)
 
@@ -737,6 +737,7 @@ end;
 
 function transformCase(c: PTransf; n: PNode): PNode;
 // removes `elif` branches of a case stmt
+// adds ``else: nil`` if needed for the code generator
 var
   len, i, j: int;
   ifs, elsen: PNode;
@@ -754,6 +755,14 @@ begin
     setLength(n.sons, i+2);
     addSon(elsen, ifs);
     n.sons[i+1] := elsen;
+  end  
+  else if (n.sons[len-1].kind <> nkElse) and 
+           not (skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
+               [tyInt..tyInt64, tyChar, tyEnum]) then begin
+    //MessageOut(renderTree(n));
+    elsen := newNodeI(nkElse, n.info);
+    addSon(elsen, newNodeI(nkNilLit, n.info));
+    addSon(n, elsen)
   end;
   result := n;
   for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]);
diff --git a/nim/wordrecg.pas b/nim/wordrecg.pas
index 9bf3de256..c18969877 100755
--- a/nim/wordrecg.pas
+++ b/nim/wordrecg.pas
@@ -63,9 +63,9 @@ type
     wMagic, wTypeCheck, wFinal, wProfiler,
     wObjChecks, wImportc, wExportc, wAlign, wNodecl, wPure,
     wVolatile, wRegister, wSideeffect, wHeader, wNosideeffect, wNoreturn,
-    wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal,
+    wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wFatal,
     wError, wWarning, wHint, wLine, wPush, wPop,
-    wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma,
+    wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wParallelBuild,
     wLink, wCompile, wLinksys, wDeprecated, wVarargs,
     wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall,
     wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure,
@@ -136,9 +136,9 @@ const
     'magic', 'typecheck', 'final', 'profiler',
     'objchecks', 'importc', 'exportc', 'align', 'nodecl', 'pure',
     'volatile', 'register', 'sideeffect', 'header', 'nosideeffect', 'noreturn',
-    'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal',
+    'merge', 'lib', 'dynlib', 'compilerproc', 'procvar', 'fatal',
     'error', 'warning', 'hint', 'line', 'push', 'pop',
-    'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma',
+    'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'parallelbuild',
     'link', 'compile', 'linksys', 'deprecated', 'varargs',
     'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall',
     'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure',
diff --git a/rod/nimrod.ini b/rod/nimrod.ini
index 6ae5f0156..78b736721 100755
--- a/rod/nimrod.ini
+++ b/rod/nimrod.ini
@@ -100,7 +100,7 @@ InstallScript: "yes"
 UninstallScript: "yes"
 
 [InnoSetup]
-path = r"c:\programme\innosetup5\iscc.exe"
+path = r"c:\program files\inno setup 5\iscc.exe"
 flags = "/Q"
 
 [C_Compiler]
diff --git a/tests/sunset.tmpl b/tests/sunset.tmpl
index 08fa50f56..6475bac4e 100755
--- a/tests/sunset.tmpl
+++ b/tests/sunset.tmpl
@@ -1,3 +1,4 @@
+#! stdtmpl
 #proc sunsetTemplate*(current, ticker, content: string,
 #                     tabs: openarray[array[0..1, string]]): string = 
 #  result = ""
diff --git a/tests/tadrdisc.nim b/tests/tadrdisc.nim
new file mode 100755
index 000000000..a7118455f
--- /dev/null
+++ b/tests/tadrdisc.nim
@@ -0,0 +1,16 @@
+# Test that the address of a dicriminants cannot be taken
+
+type
+  TKind = enum ka, kb, kc
+  TA = object
+    case k: TKind
+    of ka: x, y: int
+    of kb: a, b: string
+    of kc: c, d: float
+    
+proc setKind(k: var TKind) = 
+  k = kc
+  
+var a: TA
+setKind(a.k) #ERROR_MSG for a 'var' type a variable needs to be passed
+
diff --git a/tests/tbind1.nim b/tests/tbind1.nim
new file mode 100755
index 000000000..e7eed3e4f
--- /dev/null
+++ b/tests/tbind1.nim
@@ -0,0 +1,14 @@
+# Test the new ``bind`` keyword for templates
+
+proc p1(x: int8, y: int): int = return x + y
+
+template tempBind(x, y: expr): expr = 
+  bind p1(x, y) 
+
+proc p1(x: int, y: int8): int = return x - y
+
+# This is tricky: the call to ``p1(1'i8, 2'i8)`` should not fail in line 6, 
+# because it is not ambiguous there. But it is ambiguous after line 8. 
+
+echo tempBind(1'i8, 2'i8) #OUT 3
+
diff --git a/tests/tbind2.nim b/tests/tbind2.nim
new file mode 100755
index 000000000..41711083f
--- /dev/null
+++ b/tests/tbind2.nim
@@ -0,0 +1,10 @@
+# Test the new ``bind`` keyword for templates
+
+proc p1(x: int8, y: int): int = return x + y
+proc p1(x: int, y: int8): int = return x - y
+
+template tempBind(x, y: expr): expr = 
+  bind p1(x, y)  #ERROR_MSG ambiguous call
+
+echo tempBind(1'i8, 2'i8)
+
diff --git a/tests/tbintre2.nim b/tests/tbintre2.nim
new file mode 100755
index 000000000..dedc87705
--- /dev/null
+++ b/tests/tbintre2.nim
@@ -0,0 +1,25 @@
+# Same test, but check module boundaries
+
+import tbintree
+
+var
+  root: PBinaryTree[string]
+  x = newNode("hallo")
+add(root, x)
+add(root, "world")
+if find(root, "world"):
+  for str in items(root):
+    stdout.write(str)
+else:
+  stdout.writeln("BUG")
+
+var
+  r2: PBinaryTree[int]
+add(r2, newNode(110))
+add(r2, 223)
+add(r2, 99)
+for y in items(r2):
+  stdout.write(y)
+
+#OUT halloworld99110223
+
diff --git a/tests/tcasestm.nim b/tests/tcasestm.nim
index 16051ceb8..277b0bab1 100755
--- a/tests/tcasestm.nim
+++ b/tests/tcasestm.nim
@@ -4,29 +4,29 @@ type
   tenum = enum eA, eB, eC
 
 var
-  x: string
+  x: string = "yyy"
   y: Tenum = eA
   i: int
 
 case y
-of eA: write(stdout, "a\n")
-of eB, eC: write(stdout, "b oder c\n")
+of eA: write(stdout, "a")
+of eB, eC: write(stdout, "b or c")
 
-x = readLine(stdin)
 case x
-of "Andreas", "Rumpf": write(stdout, "Hallo Meister!\n")
-of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n")
+of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
+of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
 of "cc", "hash", "when": nil
 of "will", "it", "finally", "be", "generated": nil
-else: write(stdout, "das sollte nicht passieren!\N")
 
 case i
-of 0..5, 8, 9: nil
+of 1..5, 8, 9: nil
 of 6, 7: nil
 elif x == "Ha": 
   nil
-elif x == "Ho":
-  nil
+elif x == "yyy":
+  write(stdout, x)
 else:
   nil
 
+#OUT ayyy
+
diff --git a/tests/tcnstseq.nim b/tests/tcnstseq.nim
new file mode 100755
index 000000000..4f389bb3b
--- /dev/null
+++ b/tests/tcnstseq.nim
@@ -0,0 +1,11 @@
+# Test the new implicit conversion from sequences to arrays in a constant
+# context.
+
+import strutils
+
+const
+  myWords = "Angelika Anne Anna Anka Anja".split()
+  
+for x in items(myWords): 
+  write(stdout, x) #OUT AngelikaAnneAnnaAnkaAnja
+
diff --git a/tests/tcopy.nim b/tests/tcopy.nim
index 81d72c7f2..6cb2ec14c 100755
--- a/tests/tcopy.nim
+++ b/tests/tcopy.nim
@@ -9,7 +9,7 @@ proc main() =
   var

     a, b: string

     p: int

-  p = findSubStr("=", example)

+  p = find(example, "=")

   a = copy(example, 0, p-1)

   b = copy(example, p+1)

   writeln(stdout, a & '=' & b)

diff --git a/tests/tester.nim b/tests/tester.nim
index b09200aea..25a6dd428 100755
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -157,7 +157,7 @@ proc main(options: string) =
     var comp = callCompiler(filename, options)
     if sameResults(filename, spec, comp): inc(passed)
     inc(total)
-  # ensure that the examples at least compiles
+  # ensure that the examples at least compile
   for filename in os.walkFiles("examples/*.nim"):
     var comp = callCompiler(filename, options)
     var shortfile = os.extractFilename(filename)
diff --git a/tests/titer4.nim b/tests/titer4.nim
new file mode 100755
index 000000000..376522482
--- /dev/null
+++ b/tests/titer4.nim
@@ -0,0 +1,3 @@
+
+for x in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected
+  nil
diff --git a/tests/tmultim1.nim b/tests/tmultim1.nim
new file mode 100755
index 000000000..5d807e4c9
--- /dev/null
+++ b/tests/tmultim1.nim
@@ -0,0 +1,23 @@
+# Test multi methods
+
+type
+  TExpr = object
+  TLiteral = object of TExpr
+    x: int
+  TPlusExpr = object of TExpr
+    a, b: ref TExpr
+    
+method eval(e: ref TExpr): int = quit "to override!"
+method eval(e: ref TLiteral): int = return e.x
+method eval(e: ref TPlusExpr): int = return eval(e.a) + eval(e.b)
+
+proc newLit(x: int): ref TLiteral =
+  new(result)
+  result.x = x
+  
+proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+  new(result)
+  result.a = a
+  result.b = b
+
+echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) #OUT 7
diff --git a/tests/tmultim2.nim b/tests/tmultim2.nim
new file mode 100755
index 000000000..bf3b5fd6e
--- /dev/null
+++ b/tests/tmultim2.nim
@@ -0,0 +1,30 @@
+# Test multi methods
+
+type
+  TThing = object
+  TUnit = object of TThing
+    x: int
+  TParticle = object of TThing
+    a, b: int
+    
+method collide(a, b: TThing) {.inline.} =
+  quit "to override!"
+  
+method collide(a: TThing, b: TUnit) {.inline.} =
+  write stdout, "collide: thing, unit "
+
+method collide(a: TUnit, b: TThing) {.inline.} =
+  write stdout, "collide: unit, thing "
+
+proc test(a, b: TThing) {.inline.} =
+  collide(a, b)
+
+var
+  a: TThing
+  b, c: TUnit
+collide(b, c) # ambiguous unit, thing or thing, unit? -> prefer unit, thing!
+test(b, c)
+collide(a, b)
+#OUT collide: unit, thing collide: unit, thing collide: thing, unit
+
+
diff --git a/tests/tobjcov.nim b/tests/tobjcov.nim
new file mode 100755
index 000000000..da34fcb60
--- /dev/null
+++ b/tests/tobjcov.nim
@@ -0,0 +1,17 @@
+# Covariance is not type safe:
+
+type
+  TA = object
+    a: int
+  TB = object of TA
+    b: array[0..5000_000, int]
+    
+proc ap(x: var TA) = x.a = -1
+proc bp(x: var TB) = x.b[high(x.b)] = -1
+    
+# in Nimrod proc (x: TB) is compatible to proc (x: TA),
+# but this is not type safe:
+var f: proc (x: var TA) = bp
+var a: TA
+f(a) # bp expects a TB, but gets a TA
+
diff --git a/tests/toverl2.nim b/tests/toverl2.nim
new file mode 100755
index 000000000..2d1225c6f
--- /dev/null
+++ b/tests/toverl2.nim
@@ -0,0 +1,21 @@
+# Test new overloading resolution rules
+
+import strutils
+
+proc toverl2(x: int): string = return $x
+proc toverl2(x: bool): string = return $x
+
+iterator toverl2(x: int): int = 
+  var res = 0
+  while res < x: 
+    yield res
+    inc(res)
+    
+var
+  pp: proc (x: bool): string = toverl2
+stdout.write(pp(true))
+for x in toverl2(3): 
+  stdout.write(toverl2(x))
+stdout.write("\n")
+#OUT true012
+
diff --git a/tests/toverprc.nim b/tests/toverprc.nim
new file mode 100755
index 000000000..f35528ace
--- /dev/null
+++ b/tests/toverprc.nim
@@ -0,0 +1,25 @@
+# Test overloading of procs when used as function pointers

+

+import strutils

+
+proc parseInt(x: float): int = nil
+proc parseInt(x: bool): int = nil
+proc parseInt(x: float32): int = nil
+proc parseInt(x: int8): int = nil
+proc parseInt(x: TFile): int = nil
+proc parseInt(x: char): int = nil
+proc parseInt(x: int16): int = nil
+
+type
+  TParseInt = proc (x: string): int
+
+var
+  q = TParseInt(parseInt)
+  p: TParseInt = parseInt
+
+proc takeParseInt(x: proc (y: string): int): int = 
+  result = x("123")
+  
+echo "Give a list of numbers (separated by spaces): "
+var x = stdin.readline.split.each(parseInt).max
echo x, " is the maximum!"
echo "another number: ", takeParseInt(parseInt)
+
diff --git a/tests/tposix.nim b/tests/tposix.nim
index 87ea3acaf..bf0b49586 100755
--- a/tests/tposix.nim
+++ b/tests/tposix.nim
@@ -1,13 +1,16 @@
 # Test Posix interface
 
-import posix
+when not defined(windows):
 
-var
-  u: Tutsname
+  import posix
 
-discard uname(u)
+  var
+    u: Tutsname
+
+  discard uname(u)
+
+  writeln(stdout, u.sysname)
+  writeln(stdout, u.nodename)
+  writeln(stdout, u.release)
+  writeln(stdout, u.machine)
 
-writeln(stdout, u.sysname)
-writeln(stdout, u.nodename)
-writeln(stdout, u.release)
-writeln(stdout, u.machine)
diff --git a/tests/treadx.nim b/tests/treadx.nim
new file mode 100755
index 000000000..13acb0514
--- /dev/null
+++ b/tests/treadx.nim
@@ -0,0 +1,12 @@
+import posix
+
+var inp = ""
+var buf: array[0..10, char]
+while true:
+  var r = read(0, addr(buf), sizeof(buf)-1)
+  add inp, $buf
+  if r != sizeof(buf)-1: break
+
+echo inp
+#dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd
+
diff --git a/tests/trecinca.nim b/tests/trecinca.nim
new file mode 100755
index 000000000..d78fee233
--- /dev/null
+++ b/tests/trecinca.nim
@@ -0,0 +1,5 @@
+# Test recursive includes
+
+include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim'
+
+echo "trecina"
diff --git a/tests/trecincb.nim b/tests/trecincb.nim
new file mode 100755
index 000000000..6191671a1
--- /dev/null
+++ b/tests/trecincb.nim
@@ -0,0 +1,6 @@
+# Test recursive includes
+
+
+include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim'
+
+echo "trecinb"
diff --git a/tests/treciter.nim b/tests/treciter.nim
new file mode 100755
index 000000000..662239285
--- /dev/null
+++ b/tests/treciter.nim
@@ -0,0 +1,7 @@
+# Test that an error message occurs for a recursive iterator
+
+iterator myrec(n: int): int =
+  for x in myrec(n-1): #ERROR_MSG recursive dependency: 'myrec'
+    yield x
+
+for x in myrec(10): echo x
diff --git a/tests/tseqcon2.nim b/tests/tseqcon2.nim
new file mode 100755
index 000000000..6225c3bb1
--- /dev/null
+++ b/tests/tseqcon2.nim
@@ -0,0 +1,9 @@
+import os
+
+proc rec_dir(dir: string): seq[string] =
+  result = @[]
+  for kind, path in walk_dir(dir):
+    if kind == pcDirectory:
+      add(result, rec_dir(path))
+    else:
+      add(result, path)
diff --git a/tests/tsidee1.nim b/tests/tsidee1.nim
new file mode 100755
index 000000000..3bd520680
--- /dev/null
+++ b/tests/tsidee1.nim
@@ -0,0 +1,11 @@
+
+var
+  global: int
+
+proc dontcare(x: int): int = return x + global
+
+proc SideEffectLyer(x, y: int): int {.noSideEffect.} = #ERROR_MSG 'SideEffectLyer' can have side effects
+  return x + y + dontcare(x)
+  
+echo SideEffectLyer(1, 3) 
+
diff --git a/tests/tsidee2.nim b/tests/tsidee2.nim
new file mode 100755
index 000000000..2eaec01d7
--- /dev/null
+++ b/tests/tsidee2.nim
@@ -0,0 +1,11 @@
+
+var
+  global: int
+
+proc dontcare(x: int): int = return x
+
+proc SideEffectLyer(x, y: int): int {.noSideEffect.} = 
+  return x + y + dontcare(x)
+  
+echo SideEffectLyer(1, 3) #OUT 5
+
diff --git a/tests/tsidee3.nim b/tests/tsidee3.nim
new file mode 100755
index 000000000..be94192e7
--- /dev/null
+++ b/tests/tsidee3.nim
@@ -0,0 +1,11 @@
+
+var
+  global: int
+
+proc dontcare(x: int): int {.noSideEffect.} = return x
+
+proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSideEffect.} = 
+  return x + y + dontcare(x)
+  
+echo noSideEffect(1, 3, dontcare) #OUT 5
+
diff --git a/tests/tsidee4.nim b/tests/tsidee4.nim
new file mode 100755
index 000000000..55c474d10
--- /dev/null
+++ b/tests/tsidee4.nim
@@ -0,0 +1,10 @@
+
+var
+  global: int
+
+proc dontcare(x: int): int = return x
+
+proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSideEffect.} = 
+  return x + y + dontcare(x)
+  
+echo noSideEffect(1, 3, dontcare) #ERROR_MSG type mismatch
diff --git a/tests/tvartup.nim b/tests/tvartup.nim
new file mode 100755
index 000000000..05b00b207
--- /dev/null
+++ b/tests/tvartup.nim
@@ -0,0 +1,11 @@
+# Test the new tuple unpacking
+
+proc divmod(a, b: int): tuple[di, mo: int] =
+  return (a div b, a mod b)
+  
+var (x, y) = divmod(15, 6)
+stdout.write(x)
+stdout.write(" ")
+stdout.write(y)
+
+#OUT 2 3
diff --git a/tools/build.tmpl b/tools/build.tmpl
index 3ac9252a3..d995ca8c9 100755
--- a/tools/build.tmpl
+++ b/tools/build.tmpl
@@ -1,4 +1,4 @@
-#subsChar ?
+#! stdtmpl(subsChar='?') | standard
 #proc GenerateBuildScript(c: TConfigData): string = 
 #  result = "#! /bin/sh\n# Generated by niminst\n"
 CC="gcc"
diff --git a/tools/deinstall.tmpl b/tools/deinstall.tmpl
index b671ae80f..19b31e5b4 100755
--- a/tools/deinstall.tmpl
+++ b/tools/deinstall.tmpl
@@ -1,4 +1,4 @@
-#subsChar ?
+#! stdtmpl(subsChar='?') | standard
 #proc GenerateDeinstallScript(c: TConfigData): string = 
 #  result = "#! /bin/sh\n# Generated by niminst\n"
 #  var proj = c.name.toLower
diff --git a/tools/inno.tmpl b/tools/inno.tmpl
index 368846d66..cc8583779 100755
--- a/tools/inno.tmpl
+++ b/tools/inno.tmpl
@@ -1,3 +1,4 @@
+#! stdtmpl | standard
 #proc GenerateInnoSetup(c: TConfigData): string =
 #  result = ""
 ; Default Template for NimInst
diff --git a/tools/install.tmpl b/tools/install.tmpl
index acca8a7fb..bf3bfabc4 100755
--- a/tools/install.tmpl
+++ b/tools/install.tmpl
@@ -1,4 +1,4 @@
-#subsChar ?
+#! stdtmpl(subsChar = '?') | standard
 #proc GenerateInstallScript(c: TConfigData): string = 
 #  result = "#! /bin/sh\n# Generated by niminst\n"
 #  var proj = c.name.toLower
diff --git a/tools/niminst.nim b/tools/niminst.nim
index b1de0964c..078aba6df 100755
--- a/tools/niminst.nim
+++ b/tools/niminst.nim
@@ -152,8 +152,8 @@ proc walkDirRecursively(s: var seq[string], root: string) =
   for k, f in walkDir(root):
     case k
     of pcFile, pcLinkToFile: add(s, UnixToNativePath(f))
-    of pcDirectory: walkDirRecursively(s, f)
-    of pcLinkToDirectory: nil
+    of pcDir: walkDirRecursively(s, f)
+    of pcLinkToDir: nil
 
 proc addFiles(s: var seq[string], patterns: seq[string]) =
   for p in items(patterns):
@@ -339,9 +339,10 @@ proc setupDist(c: var TConfigData) =
     if c.innoSetup.path.len == 0:
       c.innoSetup.path = "iscc.exe"
     var outcmd = if c.outdir.len == 0: "build" else: c.outdir
-    var cmd = "$# $# /O$# $#" % [c.innoSetup.path, c.innoSetup.flags, outcmd, n]
+    var cmd = "$# $# /O$# $#" % [quoteIfContainsWhite(c.innoSetup.path),
+                                 c.innoSetup.flags, outcmd, n]
     Echo(cmd)
-    if executeShellCommand(cmd) == 0:
+    if execShellCmd(cmd) == 0:
       removeFile(n)
     else:
       quit("External program failed")
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index ef59c2fb9..bc074af74 100755
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -14,7 +14,7 @@ type
   TKeyValPair = tuple[key, val: string]
   TConfigData = object of TObject
     tabs, links: seq[TKeyValPair]
-    doc, srcdoc, webdoc: seq[string]
+    doc, srcdoc, webdoc, pdf: seq[string]
     authors, projectName, projectTitle, logo, infile, outdir, ticker: string
     vars: PStringTable
     nimrodArgs: string
@@ -25,9 +25,10 @@ proc initConfigData(c: var TConfigData) =
   c.doc = @[]
   c.srcdoc = @[]
   c.webdoc = @[]
+  c.pdf = @[]
   c.infile = ""
   c.outdir = ""
-  c.nimrodArgs = ""
+  c.nimrodArgs = "--hint[Conf]:off "
   c.authors = ""
   c.projectTitle = ""
   c.projectName = ""
@@ -65,7 +66,7 @@ proc parseCmdLine(c: var TConfigData) =
     case kind
     of cmdArgument:
       c.infile = addFileExt(key, "ini")
-      c.nimrodArgs = cmdLineRest(p)
+      c.nimrodArgs.add(cmdLineRest(p))
       break
     of cmdLongOption, cmdShortOption:
       case normalize(key)
@@ -86,8 +87,8 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) =
     of pcFile, pcLinkToFile:
       if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
         add(s, f)
-    of pcDirectory: walkDirRecursively(s, f, ext)
-    of pcLinkToDirectory: nil
+    of pcDir: walkDirRecursively(s, f, ext)
+    of pcLinkToDir: nil
 
 proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
   for p in items(patterns):
@@ -132,6 +133,7 @@ proc parseIniFile(c: var TConfigData) =
         of "documentation":
           case normalize(k.key)
           of "doc": addFiles(c.doc, "doc", ".txt", split(v, {';'}))
+          of "pdf": addFiles(c.pdf, "doc", ".txt", split(v, {';'}))
           of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'}))
           of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'}))
           else: quit(errorStr(p, "unknown variable: " & k.key))
@@ -167,11 +169,24 @@ proc buildDoc(c: var TConfigData, destPath: string) =
        [c.nimrodArgs, destPath])
 
 proc buildPdfDoc(c: var TConfigData, destPath: string) =
-  for d in items(c.doc):
-    Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d])
-    
-
-
+  if os.execShellCmd("pdflatex -version") != 0:
+    echo "pdflatex not found; not PDF documentation generated"
+  else:
+    for d in items(c.pdf):
+      Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d])
+      # call LaTeX twice to get cross references right:
+      Exec("pdflatex " & changeFileExt(d, "tex"))
+      Exec("pdflatex " & changeFileExt(d, "tex"))
+      # delete all the crappy temporary files:
+      var pdf = extractFileTrunk(d) & ".pdf"
+      moveFile(destPath / pdf, pdf)
+      removeFile(changeFileExt(pdf, "aux"))
+      if existsFile(changeFileExt(pdf, "toc")):
+        removeFile(changeFileExt(pdf, "toc"))
+      removeFile(changeFileExt(pdf, "log"))
+      removeFile(changeFileExt(pdf, "out"))
+      removeFile(changeFileExt(d, "tex"))
+  
 proc buildAddDoc(c: var TConfigData, destPath: string) =
   # build additional documentation (without the index):
   for d in items(c.webdoc):
@@ -205,6 +220,7 @@ proc main(c: var TConfigData) =
   buildAddDoc(c, "web/upload")
   buildDoc(c, "web/upload")
   buildDoc(c, "doc")
+  #buildPdfDoc(c, "doc")
 
 var c: TConfigData
 initConfigData(c)
diff --git a/tools/sunset.tmpl b/tools/sunset.tmpl
index 44f7ac486..18f8b74d5 100755
--- a/tools/sunset.tmpl
+++ b/tools/sunset.tmpl
@@ -1,3 +1,4 @@
+#! stdtmpl | standard
 #proc generateHTMLPage(c: var TConfigData, currentTab, content: string): string = 
 #  result = ""
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
diff --git a/web/download.txt b/web/download.txt
index c1a70bd63..2bb131227 100755
--- a/web/download.txt
+++ b/web/download.txt
@@ -3,8 +3,8 @@
 
 Here you can download the latest version of the Nimrod Compiler.
 Please choose your platform:
-* source-based installation: `<download/nimrod_0.8.0.zip>`_
-* installer for Windows XP/Vista (i386): `<download/nimrod_0.8.0.exe>`_
+* source-based installation: `<download/nimrod_0.8.2.zip>`_
+* installer for Windows XP/Vista (i386): `<download/nimrod_0.8.2.exe>`_
   (includes GCC and everything else you need)
 
 The source-based installation has been tested on these systems:
diff --git a/web/index.txt b/web/index.txt
index e0ca80eb3..dc9f9133a 100755
--- a/web/index.txt
+++ b/web/index.txt
@@ -75,9 +75,10 @@ Nimrod is expressive
 Nimrod is elegant
 =================
 
+* Macros can use the imperative paradigm to construct parse trees. Nimrod
+  does not require a different coding style for meta programming.
 * Macros cannot change Nimrod's syntax because there is no need for it. 
   Nimrod's syntax is flexible enough.
-* Yet Nimrod can be parsed with an LL(1) parser.
 * Statements are grouped by indentation but can span multiple lines.
   Indentation must not contain tabulators so the compiler always sees
   the code the same way as you do.
@@ -103,9 +104,8 @@ Nimrod plays nice with others
 Roadmap to 1.0
 ==============
 
-Version 0.8.4
+Version 0.8.x
   * general expressions as generic parameters
-  * multimethods
 
 Version 0.9.0
   * closures and anonymous procs
@@ -120,6 +120,6 @@ Planned features beyond 1.0
 ===========================
 
 * Threading with a transactional memory modell (the type system may be 
-  enhanced to support extensive compile-time checks for this)
-* Recursive iterators/coroutines
-* Other code generators: LLVM, EcmaScript
+  enhanced to support extensive compile-time checks for this).
+* Recursive iterators/coroutines.
+* Other code generators: LLVM, EcmaScript.
diff --git a/web/news.txt b/web/news.txt
index 65b3a5b3c..ea7c23de1 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -2,9 +2,70 @@
 News
 ====
 
+2009-10-21 Version 0.8.2 released
+=================================
+
+Bugfixes
+--------
+- Passing ``--hint[X]:off`` or ``--warning[X]:off`` as command line arguments 
+  to the compiler now works.
+- Default parameters for templates now work.
+- Fixed security issue in ``system.copy``.
+- String case without an ``else`` part generated wrong code.
+- Various fixes for the ``osproc`` module; it now works on Windows.
+
+
+Additions
+---------
+- Implemented multi-methods.
+- The compiler now detects the number of available processors and
+  executes the C compiler concurrently. This can be adjusted by the
+  ``--parallel_build: number_of_processors`` command line switch.
+- PEG-based pattern matching via the `pegs <pegs.html>`_ module.
+- Added ``system.definedInScope``.
+- Added ``system.accumulateResult``.
+- Added ``os.walkDirRec``.
+- Added ``osproc.countProcessors``.
+- Added ``osproc.execProcesses``.
+- The configuration system now supports ``$cc.options.linker``.
+- Merged ``expr`` and ``typeDesc`` in the grammar/parser: this will allow for
+  anonymous procs without ``lambda``.
+- Many scripts have no need for a GC: The new ``--gc:none`` command line switch
+  can be used to exclude the GC from the executable.
+
 
-2009-09-12 Date Version 0.8.0 released
-======================================
+Changes affecting backwards compability
+---------------------------------------
+- Taking the address of an object discriminant is now invalid.
+- Passing a proc to a proc var is only allowed for procs marked with 
+  ``procvar`` or a calling convention != ``nimcall``. For now this only causes 
+  a warning, but this will turn into an error soon.
+- ``system.&`` and ``system.add`` now receive ``openArray`` instead of ``seq``
+  parameters.
+- Removed ``strutils.findSubStr`` and ``strutils.findChars``. They have been 
+  deprecated for 3 versions.
+- The unicode module uses a distinct type for ``TRune``.
+- Deprecated ``strutils.toString``: use ``$`` instead.
+- Deprecated ``os.splitPath``: use ``os.splitPath`` that returns a tuple 
+  instead.
+- Deprecated ``os.extractDir``: use ``splitFile(path).dir`` instead.
+- Deprecated ``os.splitFilename``: use ``splitFile`` instead.
+- Deprecated ``os.extractFileExt``: use ``splitFile`` instead.
+- Deprecated ``os.extractFileTrunk``: use ``splitFile`` instead.
+- Deprecated ``os.appendFileExt``: use ``os.addFileExt`` instead.
+- Deprecated ``os.executeShellCommand``: use ``os.execShellCmd`` instead.
+- Deprecated ``os.iterOverEnvironment``: use ``os.envPairs`` instead.
+- Deprecated ``parseopt.init``: use ``parseopt.initOptParser`` instead.
+- Deprecated ``parseopt.getRestOfCommandLine``: use ``parseopt.cmdLineRest``
+  instead.
+- Deprecated ``os.pcDirectory``: use ``os.pcDir`` instead.
+- Deprecated ``os.pcLinkToDirectory``: use ``os.pcLinkToDir`` instead.
+- Deprecated ``osproc.executeProcess``: use ``osproc.execProcess`` instead.
+- Deprecated ``osproc.executeCommand``: use ``osproc.execCmd`` instead.
+
+
+2009-09-12 Version 0.8.0 released
+=================================
 
 Bugfixes
 --------
diff --git a/web/nimrod.ini b/web/nimrod.ini
index 1f58a0915..4920eda06 100755
--- a/web/nimrod.ini
+++ b/web/nimrod.ini
@@ -22,6 +22,7 @@ file: ticker
 
 [Documentation]
 doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
+pdf: "manual;lib;tut1;tut2;nimrodc"
 srcdoc: "system.nim;pure/os;pure/strutils;pure/regexprs;pure/math"
 srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
 srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase"
diff --git a/web/ticker.txt b/web/ticker.txt
index 61e5e40fd..4f33e4ce7 100755
--- a/web/ticker.txt
+++ b/web/ticker.txt
@@ -1,6 +1,9 @@
+| `2009-10-21`:newsdate:
+| Nimrod version 0.8.2 has been released!
+  Get it `here <./download.html>`_.
+
 | `2009-09-12`:newsdate:
 | Nimrod version 0.8.0 has been released!
-  Get it `here <./download.html>`_.
 
 | `2009-06-08`:newsdate:
 | Nimrod version 0.7.10 has been released!