summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <andreasrumpf@noname>2009-09-23 23:38:00 +0200
committerAndreas Rumpf <andreasrumpf@noname>2009-09-23 23:38:00 +0200
commit3f3dda5a77fa8faf6d97aa5a0ed3b0fc6e8c0918 (patch)
tree81fcf664ab2abfda200061e57fa488ae17b03192
parent66a7e3d37c0303997a6b1a3b7ec263dfb8c07748 (diff)
downloadNim-3f3dda5a77fa8faf6d97aa5a0ed3b0fc6e8c0918.tar.gz
implemented multi methods
-rwxr-xr-xdata/ast.yml2
-rwxr-xr-xdata/magic.yml1
-rwxr-xr-xdoc/intern.txt14
-rwxr-xr-xdoc/lib.txt116
-rwxr-xr-xdoc/manual.txt8
-rwxr-xr-xdoc/nimrodc.txt24
-rwxr-xr-xdoc/theindex.txt1001
-rwxr-xr-xdoc/tut1.txt38
-rwxr-xr-xdoc/tut2.txt174
-rwxr-xr-xlib/pure/macros.nim36
-rwxr-xr-xlib/pure/os.nim127
-rwxr-xr-xlib/pure/osproc.nim62
-rwxr-xr-xlib/pure/parsecfg.nim6
-rwxr-xr-xlib/pure/parseopt.nim38
-rwxr-xr-xlib/pure/parsesql.nim49
-rwxr-xr-xlib/pure/parsexml.nim4
-rwxr-xr-xlib/pure/regexprs.nim10
-rwxr-xr-xlib/pure/strutils.nim26
-rwxr-xr-xlib/pure/unicode.nim134
-rwxr-xr-xlib/system.nim59
-rwxr-xr-xlib/system/sysstr.nim26
-rwxr-xr-xlib/windows/winlean.nim214
-rw-r--r--lib/wrappers/tre/config.h259
-rw-r--r--lib/wrappers/tre/tre_all.c8873
-rw-r--r--lib/wrappers/tre/version.txt1
-rwxr-xr-xnim/ast.pas147
-rwxr-xr-xnim/ccgexprs.pas27
-rwxr-xr-xnim/ccgstmts.pas7
-rwxr-xr-xnim/ccgtypes.pas18
-rwxr-xr-xnim/cgen.pas33
-rw-r--r--nim/cgmeth.pas269
-rwxr-xr-xnim/docgen.pas1
-rwxr-xr-xnim/ecmasgen.pas2
-rwxr-xr-xnim/evals.pas20
-rwxr-xr-xnim/highlite.pas39
-rwxr-xr-xnim/importer.pas4
-rwxr-xr-xnim/lookups.pas4
-rwxr-xr-xnim/magicsys.pas16
-rwxr-xr-xnim/nhashes.pas2
-rwxr-xr-xnim/nversion.pas4
-rwxr-xr-xnim/pasparse.pas7
-rwxr-xr-xnim/passaux.pas2
-rwxr-xr-xnim/passes.pas2
-rwxr-xr-xnim/pnimsyn.pas36
-rwxr-xr-xnim/pragmas.pas6
-rwxr-xr-xnim/rnimsyn.pas4
-rwxr-xr-xnim/rodwrite.pas2
-rwxr-xr-xnim/sem.pas11
-rwxr-xr-xnim/semexprs.pas69
-rwxr-xr-xnim/semfold.pas4
-rwxr-xr-xnim/seminst.pas2
-rwxr-xr-xnim/semstmts.pas12
-rwxr-xr-xnim/semtempl.pas4
-rwxr-xr-xnim/semtypes.pas4
-rwxr-xr-xnim/sigmatch.pas24
-rwxr-xr-xnim/transf.pas18
-rwxr-xr-xtests/tester.nim97
-rwxr-xr-xtools/inno.tmpl2
-rwxr-xr-xtools/install.tmpl2
-rwxr-xr-xtools/niminst.nim10
-rwxr-xr-xtools/nimweb.nim20
-rwxr-xr-xtools/trimcc.nim7
-rwxr-xr-xweb/index.txt12
-rwxr-xr-xweb/news.txt2
-rwxr-xr-xweb/nimrod.ini6
65 files changed, 11044 insertions, 1216 deletions
diff --git a/data/ast.yml b/data/ast.yml
index 5281c9085..805b5dc68 100755
--- a/data/ast.yml
+++ b/data/ast.yml
@@ -182,6 +182,7 @@
 
   'nkModule',             # the syntax tree of a module
   'nkProcDef',            # a proc
+  'nkMethodDef',          # a method
   'nkConverterDef',       # a converter
   'nkMacroDef',           # a macro
   'nkTemplateDef',        # a template
@@ -258,6 +259,7 @@
   'skConst',              # a constant
   'skVar',                # a variable
   'skProc',               # a proc
+  'skMethod',             # a method
   'skIterator',           # an iterator
   'skConverter',          # a type converter
   'skMacro',              # a macro
diff --git a/data/magic.yml b/data/magic.yml
index 2be41d11e..d44296966 100755
--- a/data/magic.yml
+++ b/data/magic.yml
@@ -165,7 +165,6 @@
 'AppendStrCh',
 'AppendStrStr',
 'AppendSeqElem',
-'AppendSeqSeq',
 'InRange',
 'InSet',
 'Repr',
diff --git a/doc/intern.txt b/doc/intern.txt
index 1deeb4c4e..c45765b23 100755
--- a/doc/intern.txt
+++ b/doc/intern.txt
@@ -8,6 +8,8 @@
 
 .. contents::
 
+  Abstraction is layering ignorance on top of reality. -- unknown
+
 
 Directory structure
 ===================
@@ -72,18 +74,6 @@ the same::
   ./boot [-d:release]
 
 
-Coding Guidelines
-=================
-
-* Use CamelCase, not underscored_identifiers.
-* Indent with two spaces.
-* Max line length is 80 characters.
-* Provide spaces around binary operators if that enhances readability.
-* Use a space after a colon, but not before it.
-* Start types with a capital ``T``, unless they are pointers which start with
-  ``P``. 
-
-
 Pascal annotations
 ==================
 There are some annotations that the Pascal sources use so that they can
diff --git a/doc/lib.txt b/doc/lib.txt
index 6850ab515..c15303b99 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -5,6 +5,10 @@ Nimrod Standard Library
 :Author: Andreas Rumpf
 :Version: |nimrodversion|
 
+..
+
+  The good thing about reinventing the wheel is that you can get a round one.
+
 Though the Nimrod Standard Library is still evolving, it is already quite
 usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
 
@@ -12,10 +16,15 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
 while impure libraries do. A wrapper is an impure library that is a very
 low-level interface to a C library.
 
+Read this `document <apis.html>`_ for a quick overview of the API design.
+
 
 Pure libraries
 ==============
 
+Core
+----
+
 * `system <system.html>`_
   Basic procs and operators that every program needs. It also provides IO
   facilities for reading and writing text and binary files. It is imported
@@ -25,11 +34,35 @@ Pure libraries
 * `macros <macros.html>`_ 
   Contains the AST API and documentation of Nimrod for writing macros.
 
+
+String handling
+---------------
+
 * `strutils <strutils.html>`_
   This module contains common string handling operations like converting a
   string into uppercase, splitting a string into substrings, searching for
   substrings, replacing substrings.
 
+* `strtabs <strtabs.html>`_
+  The ``strtabs`` module implements an efficient hash table that is a mapping
+  from strings to strings. Supports a case-sensitive, case-insensitive and
+  style-insensitive mode. An efficient string substitution operator ``%``
+  for the string table is also provided.
+
+* `unicode <unicode.html>`_ 
+  This module provides support to handle the Unicode UTF-8 encoding.
+
+* `regexprs <regexprs.html>`_
+  This module contains procedures and operators for handling regular
+  expressions. Consider using `pegs` instead.
+
+* `pegs <pegs.html>`_
+  This module contains procedures and operators for handling PEGs.
+
+
+Generic Operating System Services
+---------------------------------
+
 * `os <os.html>`_
   Basic operating system facilities like retrieving environment variables,
   reading command line arguments, working with directories, running shell
@@ -37,7 +70,28 @@ Pure libraries
   platform independant.
 
 * `osproc <osproc.html>`_
-  Module for process communication beyond ``os.executeShellCommand``.
+  Module for process communication beyond ``os.execShellCmd``.
+
+* `times <times.html>`_
+  The ``times`` module contains basic support for working with time.
+
+* `dynlib <dynlib.html>`_
+  This module implements the ability to access symbols from shared libraries.
+
+* `streams <streams.html>`_
+  This module provides a stream interface and two implementations thereof:
+  the `PFileStream` and the `PStringStream` which implement the stream
+  interface for Nimrod file objects (`TFile`) and strings. Other modules
+  may provide other implementations for this standard stream interface.
+
+* `terminal <terminal.html>`_
+  This module contains a few procedures to control the *terminal* 
+  (also called *console*). The implementation simply uses ANSI escape 
+  sequences and does not depend on any other module.
+
+
+Math libraries
+--------------
 
 * `math <math.html>`_
   Mathematical operations like cosine, square root.
@@ -45,11 +99,17 @@ Pure libraries
 * `complex <complex.html>`_
   This module implements complex numbers and their mathematical operations.
 
-* `times <times.html>`_
-  The ``times`` module contains basic support for working with time.
 
-* `dynlib <dynlib.html>`_
-  This module implements the ability to access symbols from shared libraries.
+
+Internet Protocols and Support
+------------------------------
+
+* `cgi <cgi.html>`_
+  This module implements helpers for CGI applictions. 
+
+
+Parsers
+-------
 
 * `parseopt <parseopt.html>`_
   The ``parseopt`` module implements a command line option parser. This
@@ -71,48 +131,32 @@ Pure libraries
 
 * `parsecsv <parsecsv.html>`_ 
   The ``parsecsv`` module implements a simple high performance CSV parser.
-
-* `strtabs <strtabs.html>`_
-  The ``strtabs`` module implements an efficient hash table that is a mapping
-  from strings to strings. Supports a case-sensitive, case-insensitive and
-  style-insensitive mode. An efficient string substitution operator  ``%``
-  for the string table is also provided.
-
-* `streams <streams.html>`_
-  This module provides a stream interface and two implementations thereof:
-  the `PFileStream` and the `PStringStream` which implement the stream
-  interface for Nimrod file objects (`TFile`) and strings. Other modules
-  may provide other implementations for this standard stream interface.
-
-* `hashes <hashes.html>`_
-  This module implements efficient computations of hash values for diverse
-  Nimrod types.
+  
+* `parsesql <parsesql.html>`_
+  The ``parsesql`` module implements a simple high performance SQL parser.
 
 * `lexbase <lexbase.html>`_
   This is a low level module that implements an extremely efficient buffering
-  scheme for lexers and parsers. This is used by the ``parsecfg`` module.
+  scheme for lexers and parsers. This is used by the diverse parsing modules.
 
-* `terminal <terminal.html>`_
-  This module contains a few procedures to control the *terminal* 
-  (also called *console*). The implementation simply uses ANSI escape 
-  sequences and does not depend on any other module.
 
-* `cgi <cgi.html>`_
-  This module implements helper procs for CGI applictions. 
+Code generation
+---------------
 
-* `unicode <unicode.html>`_ 
-  This module provides support to handle the Unicode UTF-8 encoding.
+* `xmlgen <xmlgen.html>`_
+  This module implements macros for XML/HTML code generation.
 
-* `regexprs <regexprs.html>`_
-  This module contains procedures and operators for handling regular
-  expressions.
+
+Cryptography and Hashing
+------------------------
+
+* `hashes <hashes.html>`_
+  This module implements efficient computations of hash values for diverse
+  Nimrod types.
 
 * `md5 <md5.html>`_
   This module implements the MD5 checksum algorithm.
 
-* `xmlgen <xmlgen.html>`_
-  This module implements macros for HTML code generation.
-
 
 
 Impure libraries
diff --git a/doc/manual.txt b/doc/manual.txt
index e02bc1397..3c24e4b1a 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -1357,7 +1357,7 @@ given *slicelist* the ``else`` part is executed. If there is no ``else``
 part and not all possible values that ``expr`` can hold occur in a ``vallist``,
 a static error is given. This holds only for expressions of ordinal types.
 If the expression is not of an ordinal type, and no ``else`` part is
-given, control just passes after the ``case`` statement.
+given, control passes after the ``case`` statement.
 
 To suppress the static error in the ordinal case an ``else`` part with a ``nil``
 statement can be used.
@@ -1604,7 +1604,9 @@ statement is syntactic sugar for a nested block:
     continue
     stmt2
 
-  # is equivalent to:
+Is equivalent to:
+
+.. code-block:: nimrod
   while expr1:
     block myBlockName:
       stmt1
@@ -1669,7 +1671,7 @@ object on the stack and can thus reference a non-existing object.
 
 Procedures
 ~~~~~~~~~~
-What most programming languages call `methods`:idx: or `funtions`:idx: are
+What most programming languages call `methods`:idx: or `functions`:idx: are
 called `procedures`:idx: in Nimrod (which is the correct terminology). A
 procedure declaration defines an identifier and associates it with a block
 of code. A procedure may call itself recursively. The syntax is::
diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt
index 9e59ab9a4..26bf38a0b 100755
--- a/doc/nimrodc.txt
+++ b/doc/nimrodc.txt
@@ -51,8 +51,8 @@ that its extension should be ``.cfg``.
 Command line settings have priority over configuration file settings.
 
 
-Nimrod's directory structure
-----------------------------
+Generated C code directory
+--------------------------
 The generated files that Nimrod produces all go into a subdirectory called
 ``nimcache`` in your project directory. This makes it easy to delete all
 generated files.
@@ -116,7 +116,7 @@ No_decl Pragma
 The `no_decl`:idx: pragma can be applied to almost any symbol (variable, proc,
 type, etc.) and is sometimes useful for interoperability with C:
 It tells Nimrod that it should not generate a declaration for the symbol in
-the C code. Thus it makes the following possible, for example:
+the C code. For example:
 
 .. code-block:: Nimrod
   var
@@ -129,12 +129,12 @@ However, the ``header`` pragma is often the better alternative.
 Header Pragma
 ~~~~~~~~~~~~~
 The `header`:idx: pragma is very similar to the ``no_decl`` pragma: It can be
-applied to almost any symbol and specifies that not only it should not be
-declared but also that it leads to the inclusion of a given header file:
+applied to almost any symbol and specifies that it should not be declared 
+and instead the generated code should contain an ``#include``:
 
 .. code-block:: Nimrod
   type
-    PFile {.importc: "FILE*", header: "<stdio.h>".} = pointer
+    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
@@ -159,7 +159,7 @@ strings automatically:
 Line_dir Option
 ~~~~~~~~~~~~~~~
 The `line_dir`:idx: option can be turned on or off. If on the generated C code
-contains ``#line`` directives.
+contains ``#line`` directives. This may be helpful for debugging with GDB.
 
 
 Stack_trace Option
@@ -198,7 +198,7 @@ Register Pragma
 The `register`:idx: pragma is for variables only. It declares the variable as
 ``register``, giving the compiler a hint that the variable should be placed
 in a hardware register for faster access. C compilers usually ignore this
-though and for good reason: Often they do a better job without it anyway.
+though and for good reasons: Often they do a better job without it anyway.
 
 In highly specific cases (a dispatch loop of an bytecode interpreter for
 example) it may provide benefits, though.
@@ -228,7 +228,7 @@ memory, but nothing worse happens.
 Dead_code_elim Pragma
 ~~~~~~~~~~~~~~~~~~~~~
 The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the
-compiler to active (or deactivate) dead code elimination for the module the
+compiler to activate (or deactivate) dead code elimination for the module the
 pragma appers in.
 
 The ``--dead_code_elim:on`` command line switch has the same effect as marking
@@ -288,9 +288,9 @@ Optimizing string handling
 String assignments are sometimes expensive in Nimrod: They are required to
 copy the whole string. However, the compiler is often smart enough to not copy
 strings. Due to the argument passing semantics, strings are never copied when
-passed to subroutines. The compiler does not copy strings that are returned by
-a routine, because a routine returns a new string anyway. Thus it is efficient
-to do:
+passed to subroutines. The compiler does not copy strings that are result from
+a procedure call, because the called procedure returns a new string anyway.
+Thus it is efficient to do:
 
 .. code-block:: Nimrod
   var s = procA() # assignment will not copy the string; procA allocates a new
diff --git a/doc/theindex.txt b/doc/theindex.txt
index dee6ecd8c..7eb5f84e3 100755
--- a/doc/theindex.txt
+++ b/doc/theindex.txt
@@ -7,13 +7,13 @@ Index
 
 
    `!`:idx:
-     `macros.html#113 <macros.html#113>`_
+     * `pegs.html#114 <pegs.html#114>`_
+     * `macros.html#113 <macros.html#113>`_
 
    `!=`:idx:
-     `system.html#346 <system.html#346>`_
+     `system.html#347 <system.html#347>`_
 
    `$`:idx:
-     * `system.html#417 <system.html#417>`_
      * `system.html#418 <system.html#418>`_
      * `system.html#419 <system.html#419>`_
      * `system.html#420 <system.html#420>`_
@@ -21,123 +21,128 @@ Index
      * `system.html#422 <system.html#422>`_
      * `system.html#423 <system.html#423>`_
      * `system.html#424 <system.html#424>`_
+     * `system.html#425 <system.html#425>`_
      * `times.html#109 <times.html#109>`_
      * `times.html#110 <times.html#110>`_
+     * `pegs.html#148 <pegs.html#148>`_
      * `macros.html#114 <macros.html#114>`_
 
    `%`:idx:
-     * `strutils.html#111 <strutils.html#111>`_
-     * `strutils.html#112 <strutils.html#112>`_
+     * `strutils.html#109 <strutils.html#109>`_
+     * `strutils.html#110 <strutils.html#110>`_
      * `strtabs.html#112 <strtabs.html#112>`_
 
    `%%`:idx:
-     * `system.html#291 <system.html#291>`_
      * `system.html#292 <system.html#292>`_
      * `system.html#293 <system.html#293>`_
      * `system.html#294 <system.html#294>`_
      * `system.html#295 <system.html#295>`_
+     * `system.html#296 <system.html#296>`_
 
    `&`:idx:
-     * `system.html#357 <system.html#357>`_
-     * `system.html#358 <system.html#358>`_
-     * `system.html#359 <system.html#359>`_
-     * `system.html#360 <system.html#360>`_
-     * `system.html#460 <system.html#460>`_
+     * `system.html#361 <system.html#361>`_
+     * `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>`_
+     * `pegs.html#113 <pegs.html#113>`_
 
    `*`:idx:
-     * `system.html#211 <system.html#211>`_
      * `system.html#212 <system.html#212>`_
      * `system.html#213 <system.html#213>`_
      * `system.html#214 <system.html#214>`_
      * `system.html#215 <system.html#215>`_
-     * `system.html#310 <system.html#310>`_
-     * `system.html#318 <system.html#318>`_
+     * `system.html#216 <system.html#216>`_
+     * `system.html#311 <system.html#311>`_
+     * `system.html#319 <system.html#319>`_
      * `complex.html#107 <complex.html#107>`_
+     * `pegs.html#111 <pegs.html#111>`_
 
    `*%`:idx:
-     * `system.html#281 <system.html#281>`_
      * `system.html#282 <system.html#282>`_
      * `system.html#283 <system.html#283>`_
      * `system.html#284 <system.html#284>`_
      * `system.html#285 <system.html#285>`_
+     * `system.html#286 <system.html#286>`_
 
    `+`:idx:
-     * `system.html#186 <system.html#186>`_
      * `system.html#187 <system.html#187>`_
      * `system.html#188 <system.html#188>`_
      * `system.html#189 <system.html#189>`_
      * `system.html#190 <system.html#190>`_
-     * `system.html#201 <system.html#201>`_
+     * `system.html#191 <system.html#191>`_
      * `system.html#202 <system.html#202>`_
      * `system.html#203 <system.html#203>`_
      * `system.html#204 <system.html#204>`_
      * `system.html#205 <system.html#205>`_
-     * `system.html#306 <system.html#306>`_
-     * `system.html#308 <system.html#308>`_
-     * `system.html#319 <system.html#319>`_
+     * `system.html#206 <system.html#206>`_
+     * `system.html#307 <system.html#307>`_
+     * `system.html#309 <system.html#309>`_
+     * `system.html#320 <system.html#320>`_
      * `complex.html#103 <complex.html#103>`_
+     * `pegs.html#112 <pegs.html#112>`_
 
    `+%`:idx:
-     * `system.html#271 <system.html#271>`_
      * `system.html#272 <system.html#272>`_
      * `system.html#273 <system.html#273>`_
      * `system.html#274 <system.html#274>`_
      * `system.html#275 <system.html#275>`_
+     * `system.html#276 <system.html#276>`_
 
    `-`:idx:
-     * `system.html#191 <system.html#191>`_
      * `system.html#192 <system.html#192>`_
      * `system.html#193 <system.html#193>`_
      * `system.html#194 <system.html#194>`_
      * `system.html#195 <system.html#195>`_
-     * `system.html#206 <system.html#206>`_
+     * `system.html#196 <system.html#196>`_
      * `system.html#207 <system.html#207>`_
      * `system.html#208 <system.html#208>`_
      * `system.html#209 <system.html#209>`_
      * `system.html#210 <system.html#210>`_
-     * `system.html#307 <system.html#307>`_
-     * `system.html#309 <system.html#309>`_
-     * `system.html#320 <system.html#320>`_
+     * `system.html#211 <system.html#211>`_
+     * `system.html#308 <system.html#308>`_
+     * `system.html#310 <system.html#310>`_
+     * `system.html#321 <system.html#321>`_
      * `complex.html#104 <complex.html#104>`_
      * `complex.html#105 <complex.html#105>`_
      * `times.html#113 <times.html#113>`_
 
    `-%`:idx:
-     * `system.html#276 <system.html#276>`_
      * `system.html#277 <system.html#277>`_
      * `system.html#278 <system.html#278>`_
      * `system.html#279 <system.html#279>`_
      * `system.html#280 <system.html#280>`_
+     * `system.html#281 <system.html#281>`_
 
    `-+-`:idx:
-     `system.html#321 <system.html#321>`_
+     `system.html#322 <system.html#322>`_
 
    `/`:idx:
-     * `system.html#311 <system.html#311>`_
+     * `system.html#312 <system.html#312>`_
      * `os.html#124 <os.html#124>`_
      * `complex.html#106 <complex.html#106>`_
+     * `pegs.html#108 <pegs.html#108>`_
 
    `/%`:idx:
-     * `system.html#286 <system.html#286>`_
      * `system.html#287 <system.html#287>`_
      * `system.html#288 <system.html#288>`_
      * `system.html#289 <system.html#289>`_
      * `system.html#290 <system.html#290>`_
+     * `system.html#291 <system.html#291>`_
 
    `/../`:idx:
-     `os.html#127 <os.html#127>`_
+     `os.html#128 <os.html#128>`_
 
    `<`:idx:
-     * `system.html#261 <system.html#261>`_
      * `system.html#262 <system.html#262>`_
      * `system.html#263 <system.html#263>`_
      * `system.html#264 <system.html#264>`_
      * `system.html#265 <system.html#265>`_
-     * `system.html#314 <system.html#314>`_
-     * `system.html#338 <system.html#338>`_
+     * `system.html#266 <system.html#266>`_
+     * `system.html#315 <system.html#315>`_
      * `system.html#339 <system.html#339>`_
      * `system.html#340 <system.html#340>`_
      * `system.html#341 <system.html#341>`_
@@ -145,49 +150,55 @@ Index
      * `system.html#343 <system.html#343>`_
      * `system.html#344 <system.html#344>`_
      * `system.html#345 <system.html#345>`_
+     * `system.html#346 <system.html#346>`_
      * `times.html#114 <times.html#114>`_
 
    `<%`:idx:
-     * `system.html#301 <system.html#301>`_
+     `unicode.html#104 <unicode.html#104>`_
+
+   `<%`:idx:
      * `system.html#302 <system.html#302>`_
      * `system.html#303 <system.html#303>`_
      * `system.html#304 <system.html#304>`_
      * `system.html#305 <system.html#305>`_
+     * `system.html#306 <system.html#306>`_
 
    `<=`:idx:
-     * `system.html#256 <system.html#256>`_
      * `system.html#257 <system.html#257>`_
      * `system.html#258 <system.html#258>`_
      * `system.html#259 <system.html#259>`_
      * `system.html#260 <system.html#260>`_
-     * `system.html#313 <system.html#313>`_
-     * `system.html#331 <system.html#331>`_
+     * `system.html#261 <system.html#261>`_
+     * `system.html#314 <system.html#314>`_
      * `system.html#332 <system.html#332>`_
      * `system.html#333 <system.html#333>`_
      * `system.html#334 <system.html#334>`_
      * `system.html#335 <system.html#335>`_
      * `system.html#336 <system.html#336>`_
      * `system.html#337 <system.html#337>`_
+     * `system.html#338 <system.html#338>`_
 
    `<=`:idx:
      `times.html#115 <times.html#115>`_
 
    `<=%`:idx:
-     * `system.html#296 <system.html#296>`_
+     `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:
      * `md5.html#107 <md5.html#107>`_
-     * `system.html#251 <system.html#251>`_
      * `system.html#252 <system.html#252>`_
      * `system.html#253 <system.html#253>`_
      * `system.html#254 <system.html#254>`_
      * `system.html#255 <system.html#255>`_
-     * `system.html#312 <system.html#312>`_
-     * `system.html#322 <system.html#322>`_
+     * `system.html#256 <system.html#256>`_
+     * `system.html#313 <system.html#313>`_
      * `system.html#323 <system.html#323>`_
      * `system.html#324 <system.html#324>`_
      * `system.html#325 <system.html#325>`_
@@ -196,33 +207,41 @@ Index
      * `system.html#328 <system.html#328>`_
      * `system.html#329 <system.html#329>`_
      * `system.html#330 <system.html#330>`_
-     * `system.html#464 <system.html#464>`_
+     * `system.html#331 <system.html#331>`_
+     * `system.html#465 <system.html#465>`_
      * `complex.html#102 <complex.html#102>`_
+     * `unicode.html#105 <unicode.html#105>`_
      * `macros.html#115 <macros.html#115>`_
 
    `=~`:idx:
-     `regexprs.html#110 <regexprs.html#110>`_
+     `regexprs.html#111 <regexprs.html#111>`_
+
+   `=~`:idx:
+     `pegs.html#138 <pegs.html#138>`_
 
    `>`:idx:
-     `system.html#348 <system.html#348>`_
+     `system.html#349 <system.html#349>`_
 
    `>%`:idx:
-     `system.html#416 <system.html#416>`_
+     `system.html#417 <system.html#417>`_
 
    `>=`:idx:
-     `system.html#347 <system.html#347>`_
+     `system.html#348 <system.html#348>`_
 
    `>=%`:idx:
-     `system.html#415 <system.html#415>`_
+     `system.html#416 <system.html#416>`_
+
+   `?`:idx:
+     `pegs.html#110 <pegs.html#110>`_
 
    `@`:idx:
-     `system.html#356 <system.html#356>`_
+     `system.html#357 <system.html#357>`_
 
    `[]`:idx:
-     `macros.html#111 <macros.html#111>`_
+     `strtabs.html#107 <strtabs.html#107>`_
 
    `[]`:idx:
-     `strtabs.html#107 <strtabs.html#107>`_
+     `macros.html#111 <macros.html#111>`_
 
    `[]=`:idx:
      `strtabs.html#106 <strtabs.html#106>`_
@@ -237,14 +256,17 @@ Index
      `xmlgen.html#107 <xmlgen.html#107>`_
 
    `abs`:idx:
-     * `system.html#266 <system.html#266>`_
      * `system.html#267 <system.html#267>`_
      * `system.html#268 <system.html#268>`_
      * `system.html#269 <system.html#269>`_
      * `system.html#270 <system.html#270>`_
-     * `system.html#315 <system.html#315>`_
+     * `system.html#271 <system.html#271>`_
+     * `system.html#316 <system.html#316>`_
      * `complex.html#108 <complex.html#108>`_
 
+   `accumulateResult`:idx:
+     `system.html#483 <system.html#483>`_
+
    `acronym`:idx:
      `xmlgen.html#108 <xmlgen.html#108>`_
 
@@ -252,42 +274,46 @@ Index
      `nimrodc.html#113 <nimrodc.html#113>`_
 
    `add`:idx:
-     * `system.html#361 <system.html#361>`_
-     * `system.html#362 <system.html#362>`_
-     * `system.html#363 <system.html#363>`_
-     * `system.html#364 <system.html#364>`_
      * `system.html#365 <system.html#365>`_
+     * `system.html#366 <system.html#366>`_
+     * `system.html#367 <system.html#367>`_
+     * `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>`_
 
    `addf`:idx:
-     `strutils.html#113 <strutils.html#113>`_
+     `strutils.html#111 <strutils.html#111>`_
 
    `addFile`:idx:
      * `zipfiles.html#105 <zipfiles.html#105>`_
      * `zipfiles.html#106 <zipfiles.html#106>`_
      * `zipfiles.html#107 <zipfiles.html#107>`_
 
+   `addFileExt`:idx:
+     `os.html#137 <os.html#137>`_
+
    `addQuitProc`:idx:
-     `system.html#399 <system.html#399>`_
+     `system.html#403 <system.html#403>`_
 
    `address`:idx:
      `xmlgen.html#109 <xmlgen.html#109>`_
 
    `addSep`:idx:
-     `strutils.html#154 <strutils.html#154>`_
+     `strutils.html#152 <strutils.html#152>`_
 
    `alert`:idx:
      `manual.html#131 <manual.html#131>`_
 
    `allCharsInSet`:idx:
-     `strutils.html#155 <strutils.html#155>`_
+     `strutils.html#153 <strutils.html#153>`_
 
    `alloc`:idx:
-     `system.html#408 <system.html#408>`_
+     `system.html#410 <system.html#410>`_
 
    `alloc0`:idx:
-     `system.html#409 <system.html#409>`_
+     `system.html#411 <system.html#411>`_
 
    `ALLOC_MAX_BLOCK_TO_DROP`:idx:
      `mysql.html#317 <mysql.html#317>`_
@@ -299,18 +325,30 @@ Index
      `os.html#104 <os.html#104>`_
 
    `and`:idx:
-     * `system.html#120 <system.html#120>`_
-     * `system.html#236 <system.html#236>`_
+     * `system.html#121 <system.html#121>`_
      * `system.html#237 <system.html#237>`_
      * `system.html#238 <system.html#238>`_
      * `system.html#239 <system.html#239>`_
      * `system.html#240 <system.html#240>`_
+     * `system.html#241 <system.html#241>`_
+
+   `any`:idx:
+     `pegs.html#116 <pegs.html#116>`_
+
+   `any character`:idx:
+     `pegs.html#115 <pegs.html#115>`_
+
+   `any rune`:idx:
+     `pegs.html#117 <pegs.html#117>`_
+
+   `anyRune`:idx:
+     `pegs.html#118 <pegs.html#118>`_
 
    `apostrophe`:idx:
      `manual.html#129 <manual.html#129>`_
 
    `AppendFileExt`:idx:
-     `os.html#135 <os.html#135>`_
+     `os.html#138 <os.html#138>`_
 
    `arccos`:idx:
      `math.html#122 <math.html#122>`_
@@ -332,10 +370,10 @@ Index
 
    `array`:idx:
      * `tut1.html#117 <tut1.html#117>`_
-     * `system.html#128 <system.html#128>`_
+     * `system.html#129 <system.html#129>`_
 
    `array properties`:idx:
-     `tut2.html#105 <tut2.html#105>`_
+     `tut2.html#106 <tut2.html#106>`_
 
    `Arrays`:idx:
      `manual.html#153 <manual.html#153>`_
@@ -344,7 +382,7 @@ Index
      `manual.html#199 <manual.html#199>`_
 
    `assert`:idx:
-     `system.html#413 <system.html#413>`_
+     `system.html#414 <system.html#414>`_
 
    `AST`:idx:
      `macros.html#101 <macros.html#101>`_
@@ -382,10 +420,10 @@ Index
      `xmlgen.html#113 <xmlgen.html#113>`_
 
    `BiggestFloat`:idx:
-     `system.html#369 <system.html#369>`_
+     `system.html#373 <system.html#373>`_
 
    `BiggestInt`:idx:
-     `system.html#368 <system.html#368>`_
+     `system.html#372 <system.html#372>`_
 
    `BINARY_FLAG`:idx:
      `mysql.html#131 <mysql.html#131>`_
@@ -428,19 +466,22 @@ Index
      `xmlgen.html#117 <xmlgen.html#117>`_
 
    `Byte`:idx:
-     `system.html#132 <system.html#132>`_
+     `system.html#133 <system.html#133>`_
 
    `calling conventions`:idx:
      `manual.html#164 <manual.html#164>`_
 
    `capitalize`:idx:
-     `strutils.html#119 <strutils.html#119>`_
+     `strutils.html#117 <strutils.html#117>`_
 
    `caption`:idx:
      `xmlgen.html#118 <xmlgen.html#118>`_
 
+   `capture`:idx:
+     `pegs.html#121 <pegs.html#121>`_
+
    `card`:idx:
-     `system.html#174 <system.html#174>`_
+     `system.html#175 <system.html#175>`_
 
    `carriage return`:idx:
      `manual.html#122 <manual.html#122>`_
@@ -449,22 +490,22 @@ Index
      `manual.html#184 <manual.html#184>`_
 
    `cchar`:idx:
-     `system.html#370 <system.html#370>`_
+     `system.html#374 <system.html#374>`_
 
    `cdecl`:idx:
      `manual.html#166 <manual.html#166>`_
 
    `cdouble`:idx:
-     `system.html#377 <system.html#377>`_
+     `system.html#381 <system.html#381>`_
 
    `cfloat`:idx:
-     `system.html#376 <system.html#376>`_
+     `system.html#380 <system.html#380>`_
 
    `cgiError`:idx:
      `cgi.html#106 <cgi.html#106>`_
 
    `ChangeFileExt`:idx:
-     `os.html#134 <os.html#134>`_
+     `os.html#136 <os.html#136>`_
 
    `char`:idx:
      `system.html#110 <system.html#110>`_
@@ -484,6 +525,9 @@ Index
    `charData`:idx:
      `parsexml.html#109 <parsexml.html#109>`_
 
+   `charSet`:idx:
+     `pegs.html#107 <pegs.html#107>`_
+
    `CHARSET_INFO`:idx:
      `mysql.html#348 <mysql.html#348>`_
 
@@ -493,6 +537,9 @@ Index
    `checked runtime error`:idx:
      `manual.html#110 <manual.html#110>`_
 
+   `checkedSymAddr`:idx:
+     `dynlib.html#105 <dynlib.html#105>`_
+
    `check_scramble`:idx:
      `mysql.html#279 <mysql.html#279>`_
 
@@ -500,10 +547,10 @@ Index
      `mysql.html#273 <mysql.html#273>`_
 
    `chr`:idx:
-     `system.html#176 <system.html#176>`_
+     `system.html#177 <system.html#177>`_
 
    `cint`:idx:
-     `system.html#373 <system.html#373>`_
+     `system.html#377 <system.html#377>`_
 
    `cite`:idx:
      `xmlgen.html#119 <xmlgen.html#119>`_
@@ -578,13 +625,13 @@ Index
      `mysql.html#169 <mysql.html#169>`_
 
    `clong`:idx:
-     `system.html#374 <system.html#374>`_
+     `system.html#378 <system.html#378>`_
 
    `clongdouble`:idx:
-     `system.html#378 <system.html#378>`_
+     `system.html#382 <system.html#382>`_
 
    `clonglong`:idx:
-     `system.html#375 <system.html#375>`_
+     `system.html#379 <system.html#379>`_
 
    `close`:idx:
      * `lexbase.html#105 <lexbase.html#105>`_
@@ -594,29 +641,32 @@ Index
      * `zipfiles.html#103 <zipfiles.html#103>`_
 
    `Close`:idx:
-     `system.html#498 <system.html#498>`_
+     `system.html#500 <system.html#500>`_
 
    `CloseFile`:idx:
-     `system.html#497 <system.html#497>`_
+     `system.html#499 <system.html#499>`_
 
    `closure`:idx:
      `manual.html#171 <manual.html#171>`_
 
+   `cmdLineRest`:idx:
+     `parseopt.html#106 <parseopt.html#106>`_
+
    `cmp`:idx:
-     * `system.html#354 <system.html#354>`_
      * `system.html#355 <system.html#355>`_
+     * `system.html#356 <system.html#356>`_
 
    `cmpIgnoreCase`:idx:
-     `strutils.html#140 <strutils.html#140>`_
+     `strutils.html#138 <strutils.html#138>`_
 
    `cmpIgnoreStyle`:idx:
-     `strutils.html#141 <strutils.html#141>`_
+     `strutils.html#139 <strutils.html#139>`_
 
    `cmpPaths`:idx:
-     `os.html#136 <os.html#136>`_
+     `os.html#139 <os.html#139>`_
 
    `cmpRunesIgnoreCase`:idx:
-     `unicode.html#115 <unicode.html#115>`_
+     `unicode.html#120 <unicode.html#120>`_
 
    `code`:idx:
      `xmlgen.html#120 <xmlgen.html#120>`_
@@ -645,10 +695,10 @@ Index
      `mysql.html#266 <mysql.html#266>`_
 
    `CompileDate`:idx:
-     `system.html#386 <system.html#386>`_
+     `system.html#390 <system.html#390>`_
 
    `CompileTime`:idx:
-     `system.html#387 <system.html#387>`_
+     `system.html#391 <system.html#391>`_
 
    `compileTime`:idx:
      `manual.html#227 <manual.html#227>`_
@@ -667,23 +717,25 @@ Index
      * `tut1.html#104 <tut1.html#104>`_
 
    `contains`:idx:
-     * `system.html#349 <system.html#349>`_
+     * `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#143 <strutils.html#143>`_
-     * `strutils.html#144 <strutils.html#144>`_
+     * `pegs.html#139 <pegs.html#139>`_
+     * `pegs.html#140 <pegs.html#140>`_
 
    `continue`:idx:
      `manual.html#198 <manual.html#198>`_
 
    `copy`:idx:
-     * `system.html#400 <system.html#400>`_
-     * `system.html#401 <system.html#401>`_
+     * `system.html#404 <system.html#404>`_
+     * `system.html#405 <system.html#405>`_
 
    `copyFile`:idx:
-     `os.html#139 <os.html#139>`_
+     `os.html#142 <os.html#142>`_
 
    `copyMem`:idx:
-     `system.html#405 <system.html#405>`_
+     `system.html#407 <system.html#407>`_
 
    `copyNimNode`:idx:
      `macros.html#134 <macros.html#134>`_
@@ -704,32 +756,32 @@ Index
      `math.html#109 <math.html#109>`_
 
    `countdown`:idx:
-     `system.html#434 <system.html#434>`_
+     `system.html#435 <system.html#435>`_
 
    `countup`:idx:
-     `system.html#435 <system.html#435>`_
+     `system.html#436 <system.html#436>`_
 
    `cpuEndian`:idx:
-     `system.html#392 <system.html#392>`_
+     `system.html#396 <system.html#396>`_
 
    `createDir`:idx:
-     * `os.html#151 <os.html#151>`_
+     * `os.html#156 <os.html#156>`_
      * `zipfiles.html#104 <zipfiles.html#104>`_
 
    `create_random_string`:idx:
      `mysql.html#269 <mysql.html#269>`_
 
    `cschar`:idx:
-     `system.html#371 <system.html#371>`_
+     `system.html#375 <system.html#375>`_
 
    `cshort`:idx:
-     `system.html#372 <system.html#372>`_
+     `system.html#376 <system.html#376>`_
 
    `cstring`:idx:
      `system.html#112 <system.html#112>`_
 
    `cstringArray`:idx:
-     `system.html#379 <system.html#379>`_
+     `system.html#383 <system.html#383>`_
 
    `CSV`:idx:
      `parsecsv.html#101 <parsecsv.html#101>`_
@@ -1104,7 +1156,7 @@ Index
      `manual.html#179 <manual.html#179>`_
 
    `dbgLineHook`:idx:
-     `system.html#430 <system.html#430>`_
+     `system.html#431 <system.html#431>`_
 
    `dd`:idx:
      `xmlgen.html#123 <xmlgen.html#123>`_
@@ -1113,13 +1165,13 @@ Index
      `nimrodc.html#114 <nimrodc.html#114>`_
 
    `dealloc`:idx:
-     `system.html#411 <system.html#411>`_
+     `system.html#413 <system.html#413>`_
 
    `debugger`:idx:
      `nimrodc.html#110 <nimrodc.html#110>`_
 
    `dec`:idx:
-     `system.html#165 <system.html#165>`_
+     `system.html#166 <system.html#166>`_
 
    `decodeData`:idx:
      `cgi.html#107 <cgi.html#107>`_
@@ -1127,16 +1179,19 @@ Index
    `defined`:idx:
      `system.html#118 <system.html#118>`_
 
+   `definedInScope`:idx:
+     `system.html#119 <system.html#119>`_
+
    `del`:idx:
      * `xmlgen.html#124 <xmlgen.html#124>`_
      * `macros.html#119 <macros.html#119>`_
 
    `delete`:idx:
-     `strutils.html#161 <strutils.html#161>`_
+     `strutils.html#159 <strutils.html#159>`_
 
    `deleteStr`:idx:
-     * `strutils.html#129 <strutils.html#129>`_
-     * `strutils.html#162 <strutils.html#162>`_
+     * `strutils.html#127 <strutils.html#127>`_
+     * `strutils.html#160 <strutils.html#160>`_
 
    `dfn`:idx:
      `xmlgen.html#125 <xmlgen.html#125>`_
@@ -1144,6 +1199,9 @@ Index
    `Digits`:idx:
      `strutils.html#104 <strutils.html#104>`_
 
+   `digits`:idx:
+     `pegs.html#125 <pegs.html#125>`_
+
    `DirSep`:idx:
      `os.html#103 <os.html#103>`_
 
@@ -1151,11 +1209,11 @@ Index
      `manual.html#180 <manual.html#180>`_
 
    `div`:idx:
-     * `system.html#216 <system.html#216>`_
      * `system.html#217 <system.html#217>`_
      * `system.html#218 <system.html#218>`_
      * `system.html#219 <system.html#219>`_
      * `system.html#220 <system.html#220>`_
+     * `system.html#221 <system.html#221>`_
      * `xmlgen.html#126 <xmlgen.html#126>`_
 
    `dl`:idx:
@@ -1180,64 +1238,70 @@ Index
      `math.html#102 <math.html#102>`_
 
    `EAccessViolation`:idx:
-     `system.html#148 <system.html#148>`_
+     `system.html#149 <system.html#149>`_
 
    `each`:idx:
-     `system.html#467 <system.html#467>`_
+     `system.html#468 <system.html#468>`_
 
    `EArithmetic`:idx:
-     `system.html#145 <system.html#145>`_
+     `system.html#146 <system.html#146>`_
 
    `EAssertionFailed`:idx:
-     `system.html#149 <system.html#149>`_
+     `system.html#150 <system.html#150>`_
 
    `EAsynch`:idx:
-     `system.html#138 <system.html#138>`_
+     `system.html#139 <system.html#139>`_
 
    `E_Base`:idx:
-     `system.html#137 <system.html#137>`_
+     `system.html#138 <system.html#138>`_
 
    `ECgi`:idx:
      `cgi.html#104 <cgi.html#104>`_
 
    `echo`:idx:
-     `system.html#482 <system.html#482>`_
+     `system.html#484 <system.html#484>`_
 
    `EControlC`:idx:
-     `system.html#150 <system.html#150>`_
+     `system.html#151 <system.html#151>`_
 
    `editDistance`:idx:
-     `strutils.html#168 <strutils.html#168>`_
+     `strutils.html#166 <strutils.html#166>`_
 
    `EDivByZero`:idx:
-     `system.html#146 <system.html#146>`_
+     `system.html#147 <system.html#147>`_
 
    `EInvalidCsv`:idx:
      `parsecsv.html#105 <parsecsv.html#105>`_
 
    `EInvalidField`:idx:
-     `system.html#154 <system.html#154>`_
+     `system.html#155 <system.html#155>`_
 
    `EInvalidIndex`:idx:
-     `system.html#153 <system.html#153>`_
+     `system.html#154 <system.html#154>`_
 
    `EInvalidLibrary`:idx:
-     `system.html#143 <system.html#143>`_
+     `system.html#144 <system.html#144>`_
 
    `EInvalidObjectAssignment`:idx:
-     `system.html#158 <system.html#158>`_
+     `system.html#159 <system.html#159>`_
 
    `EInvalidObjectConversion`:idx:
-     `system.html#159 <system.html#159>`_
+     `system.html#160 <system.html#160>`_
+
+   `EInvalidPeg`:idx:
+     `pegs.html#149 <pegs.html#149>`_
 
    `EInvalidRegEx`:idx:
-     `regexprs.html#103 <regexprs.html#103>`_
+     `regexprs.html#104 <regexprs.html#104>`_
+
+   `EInvalidSql`:idx:
+     `parsesql.html#103 <parsesql.html#103>`_
 
    `EInvalidValue`:idx:
-     `system.html#151 <system.html#151>`_
+     `system.html#152 <system.html#152>`_
 
    `EIO`:idx:
-     `system.html#141 <system.html#141>`_
+     `system.html#142 <system.html#142>`_
 
    `elementName`:idx:
      `parsexml.html#111 <parsexml.html#111>`_
@@ -1252,15 +1316,16 @@ Index
      `endb.html#102 <endb.html#102>`_
 
    `EndOfFile`:idx:
-     * `system.html#499 <system.html#499>`_
+     * `system.html#501 <system.html#501>`_
      * `lexbase.html#101 <lexbase.html#101>`_
 
    `endsWith`:idx:
-     `strutils.html#153 <strutils.html#153>`_
+     * `strutils.html#151 <strutils.html#151>`_
+     * `pegs.html#142 <pegs.html#142>`_
 
    `ENoExceptionToReraise`:idx:
      * `manual.html#187 <manual.html#187>`_
-     * `system.html#157 <system.html#157>`_
+     * `system.html#158 <system.html#158>`_
 
    `entityName`:idx:
      `parsexml.html#112 <parsexml.html#112>`_
@@ -1268,12 +1333,12 @@ Index
    `enum_cursor_type`:idx:
      `mysql.html#237 <mysql.html#237>`_
 
-   `enumeration`:idx:
-     `tut1.html#113 <tut1.html#113>`_
-
    `Enumeration`:idx:
      `manual.html#149 <manual.html#149>`_
 
+   `enumeration`:idx:
+     `tut1.html#113 <tut1.html#113>`_
+
    `enum_field_types`:idx:
      `mysql.html#202 <mysql.html#202>`_
 
@@ -1292,22 +1357,25 @@ Index
    `enum_stmt_attr_type`:idx:
      `mysql.html#383 <mysql.html#383>`_
 
+   `envPairs`:idx:
+     `os.html#151 <os.html#151>`_
+
    `EOS`:idx:
-     `system.html#142 <system.html#142>`_
+     `system.html#143 <system.html#143>`_
 
    `EOutOfMemory`:idx:
-     `system.html#152 <system.html#152>`_
+     `system.html#153 <system.html#153>`_
 
    `EOutOfRange`:idx:
      * `manual.html#146 <manual.html#146>`_
      * `tut1.html#112 <tut1.html#112>`_
-     * `system.html#155 <system.html#155>`_
+     * `system.html#156 <system.html#156>`_
 
    `EOverflow`:idx:
-     `system.html#147 <system.html#147>`_
+     `system.html#148 <system.html#148>`_
 
    `equalMem`:idx:
-     `system.html#407 <system.html#407>`_
+     `system.html#409 <system.html#409>`_
 
    `EraseLine`:idx:
      `terminal.html#108 <terminal.html#108>`_
@@ -1316,7 +1384,7 @@ Index
      `terminal.html#109 <terminal.html#109>`_
 
    `ERessourceExhausted`:idx:
-     `system.html#144 <system.html#144>`_
+     `system.html#145 <system.html#145>`_
 
    `error`:idx:
      * `manual.html#225 <manual.html#225>`_
@@ -1333,23 +1401,23 @@ Index
      `parsecfg.html#110 <parsecfg.html#110>`_
 
    `errorStream`:idx:
-     `osproc.html#114 <osproc.html#114>`_
+     `osproc.html#116 <osproc.html#116>`_
 
    `escape`:idx:
      * `manual.html#133 <manual.html#133>`_
-     * `strutils.html#165 <strutils.html#165>`_
+     * `strutils.html#163 <strutils.html#163>`_
 
    `escape sequences`:idx:
      `manual.html#120 <manual.html#120>`_
 
    `EStackOverflow`:idx:
-     `system.html#156 <system.html#156>`_
+     `system.html#157 <system.html#157>`_
 
    `ESynch`:idx:
-     `system.html#139 <system.html#139>`_
+     `system.html#140 <system.html#140>`_
 
    `ESystem`:idx:
-     `system.html#140 <system.html#140>`_
+     `system.html#141 <system.html#141>`_
 
    `eventAttr`:idx:
      `xmlgen.html#104 <xmlgen.html#104>`_
@@ -1361,23 +1429,31 @@ Index
      `manual.html#189 <manual.html#189>`_
 
    `exceptions`:idx:
-     `tut2.html#106 <tut2.html#106>`_
+     `tut2.html#107 <tut2.html#107>`_
 
    `excl`:idx:
-     `system.html#173 <system.html#173>`_
+     `system.html#174 <system.html#174>`_
 
    `exclFilePermissions`:idx:
-     `os.html#157 <os.html#157>`_
+     `os.html#162 <os.html#162>`_
+
+   `execCmd`:idx:
+     `osproc.html#105 <osproc.html#105>`_
+
+   `execProcess`:idx:
+     `osproc.html#103 <osproc.html#103>`_
+
+   `execShellCmd`:idx:
+     `os.html#146 <os.html#146>`_
 
    `executeCommand`:idx:
-     `osproc.html#104 <osproc.html#104>`_
+     `osproc.html#106 <osproc.html#106>`_
 
    `executeProcess`:idx:
-     * `osproc.html#103 <osproc.html#103>`_
-     * `osproc.html#115 <osproc.html#115>`_
+     `osproc.html#104 <osproc.html#104>`_
 
    `executeShellCommand`:idx:
-     `os.html#142 <os.html#142>`_
+     `os.html#145 <os.html#145>`_
 
    `ExeExt`:idx:
      `os.html#107 <os.html#107>`_
@@ -1386,7 +1462,7 @@ Index
      `os.html#115 <os.html#115>`_
 
    `existsEnv`:idx:
-     `os.html#144 <os.html#144>`_
+     `os.html#148 <os.html#148>`_
 
    `existsFile`:idx:
      `os.html#114 <os.html#114>`_
@@ -1395,7 +1471,7 @@ Index
      `math.html#119 <math.html#119>`_
 
    `expandFilename`:idx:
-     `os.html#130 <os.html#130>`_
+     `os.html#132 <os.html#132>`_
 
    `expectKind`:idx:
      `macros.html#145 <macros.html#145>`_
@@ -1413,19 +1489,19 @@ Index
      `system.html#115 <system.html#115>`_
 
    `expression macros`:idx:
-     `tut2.html#110 <tut2.html#110>`_
+     `tut2.html#111 <tut2.html#111>`_
 
    `extractDir`:idx:
-     `os.html#128 <os.html#128>`_
+     `os.html#130 <os.html#130>`_
 
    `extractFileExt`:idx:
-     `os.html#132 <os.html#132>`_
+     `os.html#134 <os.html#134>`_
 
    `extractFilename`:idx:
-     `os.html#129 <os.html#129>`_
+     `os.html#131 <os.html#131>`_
 
    `extractFileTrunk`:idx:
-     `os.html#133 <os.html#133>`_
+     `os.html#135 <os.html#135>`_
 
    `ExtSep`:idx:
      `os.html#109 <os.html#109>`_
@@ -1436,6 +1512,9 @@ Index
    `fastcall`:idx:
      `manual.html#169 <manual.html#169>`_
 
+   `fastRuneAt`:idx:
+     `unicode.html#108 <unicode.html#108>`_
+
    `fatal`:idx:
      `manual.html#229 <manual.html#229>`_
 
@@ -1527,7 +1606,7 @@ Index
      `mysql.html#218 <mysql.html#218>`_
 
    `fileHandle`:idx:
-     `system.html#523 <system.html#523>`_
+     `system.html#525 <system.html#525>`_
 
    `fileNewer`:idx:
      `os.html#119 <os.html#119>`_
@@ -1545,19 +1624,21 @@ Index
      `manual.html#191 <manual.html#191>`_
 
    `find`:idx:
-     * `system.html#465 <system.html#465>`_
+     * `system.html#466 <system.html#466>`_
+     * `strutils.html#122 <strutils.html#122>`_
+     * `strutils.html#123 <strutils.html#123>`_
      * `strutils.html#124 <strutils.html#124>`_
-     * `strutils.html#125 <strutils.html#125>`_
-     * `strutils.html#126 <strutils.html#126>`_
-     * `regexprs.html#108 <regexprs.html#108>`_
      * `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#123 <strutils.html#123>`_
+     `strutils.html#121 <strutils.html#121>`_
 
    `findSubStr`:idx:
-     * `strutils.html#121 <strutils.html#121>`_
-     * `strutils.html#122 <strutils.html#122>`_
+     * `strutils.html#119 <strutils.html#119>`_
+     * `strutils.html#120 <strutils.html#120>`_
 
    `float`:idx:
      `system.html#106 <system.html#106>`_
@@ -1575,7 +1656,7 @@ Index
      `macros.html#128 <macros.html#128>`_
 
    `FlushFile`:idx:
-     `system.html#501 <system.html#501>`_
+     `system.html#503 <system.html#503>`_
 
    `for`:idx:
      * `manual.html#207 <manual.html#207>`_
@@ -1600,39 +1681,39 @@ Index
    `FUNCTIONPOINT`:idx:
      `libcurl.html#265 <libcurl.html#265>`_
 
-   `funtions`:idx:
+   `functions`:idx:
      `manual.html#201 <manual.html#201>`_
 
    `GC_disable`:idx:
-     `system.html#468 <system.html#468>`_
+     `system.html#469 <system.html#469>`_
 
    `GC_disableMarkAndSweep`:idx:
-     `system.html#474 <system.html#474>`_
+     `system.html#475 <system.html#475>`_
 
    `GC_enable`:idx:
-     `system.html#469 <system.html#469>`_
+     `system.html#470 <system.html#470>`_
 
    `GC_enableMarkAndSweep`:idx:
-     `system.html#473 <system.html#473>`_
+     `system.html#474 <system.html#474>`_
 
    `GC_fullCollect`:idx:
-     `system.html#470 <system.html#470>`_
+     `system.html#471 <system.html#471>`_
 
    `GC_getStatistics`:idx:
-     `system.html#475 <system.html#475>`_
+     `system.html#476 <system.html#476>`_
 
    `GC_ref`:idx:
-     * `system.html#476 <system.html#476>`_
      * `system.html#477 <system.html#477>`_
      * `system.html#478 <system.html#478>`_
+     * `system.html#479 <system.html#479>`_
 
    `GC_setStrategy`:idx:
-     `system.html#472 <system.html#472>`_
+     `system.html#473 <system.html#473>`_
 
    `GC_unref`:idx:
-     * `system.html#479 <system.html#479>`_
      * `system.html#480 <system.html#480>`_
      * `system.html#481 <system.html#481>`_
+     * `system.html#482 <system.html#482>`_
 
    `generalized raw string literal`:idx:
      `manual.html#136 <manual.html#136>`_
@@ -1642,13 +1723,13 @@ Index
 
    `Generics`:idx:
      * `manual.html#211 <manual.html#211>`_
-     * `tut2.html#108 <tut2.html#108>`_
+     * `tut2.html#109 <tut2.html#109>`_
 
    `getApplicationDir`:idx:
-     `os.html#161 <os.html#161>`_
+     `os.html#166 <os.html#166>`_
 
    `getApplicationFilename`:idx:
-     `os.html#160 <os.html#160>`_
+     `os.html#165 <os.html#165>`_
 
    `getClockStr`:idx:
      `times.html#112 <times.html#112>`_
@@ -1661,7 +1742,7 @@ Index
      * `parsexml.html#117 <parsexml.html#117>`_
 
    `getConfigDir`:idx:
-     `os.html#159 <os.html#159>`_
+     `os.html#164 <os.html#164>`_
 
    `getContentLength`:idx:
      `cgi.html#110 <cgi.html#110>`_
@@ -1676,7 +1757,7 @@ Index
      `os.html#120 <os.html#120>`_
 
    `getCurrentExceptionMsg`:idx:
-     `system.html#426 <system.html#426>`_
+     `system.html#427 <system.html#427>`_
 
    `getCurrentLine`:idx:
      `lexbase.html#106 <lexbase.html#106>`_
@@ -1688,23 +1769,23 @@ Index
      `cgi.html#112 <cgi.html#112>`_
 
    `getEnv`:idx:
-     `os.html#143 <os.html#143>`_
+     `os.html#147 <os.html#147>`_
 
    `getFilename`:idx:
      * `parsecfg.html#109 <parsecfg.html#109>`_
      * `parsexml.html#119 <parsexml.html#119>`_
 
    `getFilePermissions`:idx:
-     `os.html#154 <os.html#154>`_
+     `os.html#159 <os.html#159>`_
 
    `getFilePos`:idx:
-     `system.html#521 <system.html#521>`_
+     `system.html#523 <system.html#523>`_
 
    `getFileSize`:idx:
-     `system.html#513 <system.html#513>`_
+     `system.html#515 <system.html#515>`_
 
    `getFreeMem`:idx:
-     `system.html#432 <system.html#432>`_
+     `system.html#433 <system.html#433>`_
 
    `getGatewayInterface`:idx:
      `cgi.html#113 <cgi.html#113>`_
@@ -1713,7 +1794,7 @@ Index
      `times.html#107 <times.html#107>`_
 
    `getHomeDir`:idx:
-     `os.html#158 <os.html#158>`_
+     `os.html#163 <os.html#163>`_
 
    `getHttpAccept`:idx:
      `cgi.html#114 <cgi.html#114>`_
@@ -1759,10 +1840,10 @@ Index
      `md5.html#106 <md5.html#106>`_
 
    `getOccupiedMem`:idx:
-     `system.html#431 <system.html#431>`_
+     `system.html#432 <system.html#432>`_
 
    `getopt`:idx:
-     `parseopt.html#106 <parseopt.html#106>`_
+     `parseopt.html#108 <parseopt.html#108>`_
 
    `getPathInfo`:idx:
      `cgi.html#123 <cgi.html#123>`_
@@ -1774,7 +1855,7 @@ Index
      `cgi.html#125 <cgi.html#125>`_
 
    `getRefcount`:idx:
-     `system.html#425 <system.html#425>`_
+     `system.html#426 <system.html#426>`_
 
    `getRemoteAddr`:idx:
      `cgi.html#126 <cgi.html#126>`_
@@ -1798,7 +1879,7 @@ Index
      `cgi.html#132 <cgi.html#132>`_
 
    `getRestOfCommandLine`:idx:
-     `parseopt.html#105 <parseopt.html#105>`_
+     `parseopt.html#107 <parseopt.html#107>`_
 
    `get_salt_from_password`:idx:
      `mysql.html#280 <mysql.html#280>`_
@@ -1843,7 +1924,7 @@ Index
      `times.html#105 <times.html#105>`_
 
    `getTotalMem`:idx:
-     `system.html#433 <system.html#433>`_
+     `system.html#434 <system.html#434>`_
 
    `get_tty_password`:idx:
      `mysql.html#282 <mysql.html#282>`_
@@ -1907,7 +1988,7 @@ Index
      `nimrodc.html#105 <nimrodc.html#105>`_
 
    `high`:idx:
-     `system.html#125 <system.html#125>`_
+     `system.html#126 <system.html#126>`_
 
    `hint`:idx:
      * `manual.html#223 <manual.html#223>`_
@@ -1915,13 +1996,13 @@ Index
      * `macros.html#138 <macros.html#138>`_
 
    `hostCPU`:idx:
-     `system.html#394 <system.html#394>`_
+     `system.html#398 <system.html#398>`_
 
    `HOSTNAME_LENGTH`:idx:
      `mysql.html#111 <mysql.html#111>`_
 
    `hostOS`:idx:
-     `system.html#393 <system.html#393>`_
+     `system.html#397 <system.html#397>`_
 
    `hr`:idx:
      `xmlgen.html#140 <xmlgen.html#140>`_
@@ -1958,11 +2039,15 @@ Index
      `xmlgen.html#141 <xmlgen.html#141>`_
 
    `ident`:idx:
-     `macros.html#124 <macros.html#124>`_
+     * `pegs.html#129 <pegs.html#129>`_
+     * `macros.html#124 <macros.html#124>`_
 
    `ident=`:idx:
      `macros.html#130 <macros.html#130>`_
 
+   `identChars`:idx:
+     `pegs.html#127 <pegs.html#127>`_
+
    `IdentChars`:idx:
      `strutils.html#105 <strutils.html#105>`_
 
@@ -1975,6 +2060,9 @@ Index
    `IdentStartChars`:idx:
      `strutils.html#106 <strutils.html#106>`_
 
+   `identStartChars`:idx:
+     `pegs.html#128 <pegs.html#128>`_
+
    `if`:idx:
      `manual.html#183 <manual.html#183>`_
 
@@ -1992,16 +2080,16 @@ Index
      `nimrodc.html#101 <nimrodc.html#101>`_
 
    `in`:idx:
-     `system.html#350 <system.html#350>`_
+     `system.html#351 <system.html#351>`_
 
    `inc`:idx:
-     `system.html#164 <system.html#164>`_
+     `system.html#165 <system.html#165>`_
 
    `incl`:idx:
-     `system.html#172 <system.html#172>`_
+     `system.html#173 <system.html#173>`_
 
    `inclFilePermissions`:idx:
-     `os.html#156 <os.html#156>`_
+     `os.html#161 <os.html#161>`_
 
    `include`:idx:
      `tut1.html#129 <tut1.html#129>`_
@@ -2010,13 +2098,16 @@ Index
      `manual.html#113 <manual.html#113>`_
 
    `inf`:idx:
-     `system.html#427 <system.html#427>`_
+     `system.html#428 <system.html#428>`_
 
    `information hiding`:idx:
      * `manual.html#217 <manual.html#217>`_
      * `tut1.html#126 <tut1.html#126>`_
 
    `init`:idx:
+     `parseopt.html#104 <parseopt.html#104>`_
+
+   `initOptParser`:idx:
      `parseopt.html#103 <parseopt.html#103>`_
 
    `inline`:idx:
@@ -2026,7 +2117,7 @@ Index
      `xmlgen.html#143 <xmlgen.html#143>`_
 
    `inputStream`:idx:
-     `osproc.html#112 <osproc.html#112>`_
+     `osproc.html#114 <osproc.html#114>`_
 
    `ins`:idx:
      `xmlgen.html#144 <xmlgen.html#144>`_
@@ -2050,7 +2141,7 @@ Index
      `mysql.html#306 <mysql.html#306>`_
 
    `intToStr`:idx:
-     `strutils.html#146 <strutils.html#146>`_
+     `strutils.html#144 <strutils.html#144>`_
 
    `intVal`:idx:
      `macros.html#121 <macros.html#121>`_
@@ -2059,30 +2150,30 @@ Index
      `macros.html#127 <macros.html#127>`_
 
    `is`:idx:
-     `system.html#352 <system.html#352>`_
+     `system.html#353 <system.html#353>`_
 
    `isAlpha`:idx:
-     `unicode.html#111 <unicode.html#111>`_
+     `unicode.html#116 <unicode.html#116>`_
 
    `IS_BLOB`:idx:
      `mysql.html#304 <mysql.html#304>`_
 
    `isLower`:idx:
-     `unicode.html#109 <unicode.html#109>`_
+     `unicode.html#114 <unicode.html#114>`_
 
    `isMainModule`:idx:
-     `system.html#385 <system.html#385>`_
+     `system.html#389 <system.html#389>`_
 
    `isNil`:idx:
-     * `system.html#454 <system.html#454>`_
      * `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>`_
 
    `is_not`:idx:
-     `system.html#353 <system.html#353>`_
+     `system.html#354 <system.html#354>`_
 
    `IS_NOT_NULL`:idx:
      `mysql.html#303 <mysql.html#303>`_
@@ -2100,34 +2191,34 @@ Index
      `mysql.html#302 <mysql.html#302>`_
 
    `isTitle`:idx:
-     `unicode.html#112 <unicode.html#112>`_
+     `unicode.html#117 <unicode.html#117>`_
 
    `isUpper`:idx:
-     `unicode.html#110 <unicode.html#110>`_
+     `unicode.html#115 <unicode.html#115>`_
 
    `isWhiteSpace`:idx:
-     `unicode.html#113 <unicode.html#113>`_
+     `unicode.html#118 <unicode.html#118>`_
 
    `Item_result`:idx:
      `mysql.html#255 <mysql.html#255>`_
 
    `items`:idx:
-     * `system.html#448 <system.html#448>`_
      * `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>`_
 
    `iterator`:idx:
      `manual.html#208 <manual.html#208>`_
 
    `iterOverEnvironment`:idx:
-     `os.html#146 <os.html#146>`_
+     `os.html#150 <os.html#150>`_
 
    `join`:idx:
-     * `strutils.html#157 <strutils.html#157>`_
-     * `strutils.html#158 <strutils.html#158>`_
+     * `strutils.html#155 <strutils.html#155>`_
+     * `strutils.html#156 <strutils.html#156>`_
 
    `JoinPath`:idx:
      * `os.html#122 <os.html#122>`_
@@ -2153,17 +2244,21 @@ Index
      `xmlgen.html#147 <xmlgen.html#147>`_
 
    `len`:idx:
-     * `system.html#167 <system.html#167>`_
      * `system.html#168 <system.html#168>`_
      * `system.html#169 <system.html#169>`_
      * `system.html#170 <system.html#170>`_
      * `system.html#171 <system.html#171>`_
+     * `system.html#172 <system.html#172>`_
      * `strtabs.html#109 <strtabs.html#109>`_
+     * `parsesql.html#107 <parsesql.html#107>`_
      * `macros.html#116 <macros.html#116>`_
 
    `Letters`:idx:
      `strutils.html#103 <strutils.html#103>`_
 
+   `letters`:idx:
+     `pegs.html#124 <pegs.html#124>`_
+
    `li`:idx:
      `xmlgen.html#148 <xmlgen.html#148>`_
 
@@ -2189,7 +2284,7 @@ Index
      `nimrodc.html#107 <nimrodc.html#107>`_
 
    `lines`:idx:
-     `system.html#522 <system.html#522>`_
+     `system.html#524 <system.html#524>`_
 
    `line_trace`:idx:
      `nimrodc.html#109 <nimrodc.html#109>`_
@@ -2203,6 +2298,9 @@ Index
    `load_defaults`:idx:
      `mysql.html#285 <mysql.html#285>`_
 
+   `LoadLib`:idx:
+     `dynlib.html#102 <dynlib.html#102>`_
+
    `local type inference`:idx:
      `tut1.html#101 <tut1.html#101>`_
 
@@ -2225,7 +2323,7 @@ Index
      `math.html#118 <math.html#118>`_
 
    `low`:idx:
-     `system.html#126 <system.html#126>`_
+     `system.html#127 <system.html#127>`_
 
    `Macros`:idx:
      `manual.html#214 <manual.html#214>`_
@@ -2261,20 +2359,24 @@ Index
      `xmlgen.html#150 <xmlgen.html#150>`_
 
    `match`:idx:
-     * `regexprs.html#105 <regexprs.html#105>`_
      * `regexprs.html#106 <regexprs.html#106>`_
+     * `regexprs.html#107 <regexprs.html#107>`_
+     * `pegs.html#132 <pegs.html#132>`_
+     * `pegs.html#133 <pegs.html#133>`_
 
    `matchLen`:idx:
-     `regexprs.html#107 <regexprs.html#107>`_
+     * `regexprs.html#108 <regexprs.html#108>`_
+     * `pegs.html#134 <pegs.html#134>`_
+     * `pegs.html#135 <pegs.html#135>`_
 
    `max`:idx:
-     * `system.html#317 <system.html#317>`_
-     * `system.html#442 <system.html#442>`_
+     * `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>`_
 
    `MAX_BIGINT_WIDTH`:idx:
      `mysql.html#194 <mysql.html#194>`_
@@ -2301,7 +2403,8 @@ Index
      `mysql.html#191 <mysql.html#191>`_
 
    `MaxSubpatterns`:idx:
-     `regexprs.html#104 <regexprs.html#104>`_
+     * `regexprs.html#105 <regexprs.html#105>`_
+     * `pegs.html#131 <pegs.html#131>`_
 
    `MAX_TINYINT_WIDTH`:idx:
      `mysql.html#190 <mysql.html#190>`_
@@ -2331,26 +2434,26 @@ Index
      `xmlgen.html#151 <xmlgen.html#151>`_
 
    `method call syntax`:idx:
-     `tut2.html#104 <tut2.html#104>`_
+     `tut2.html#105 <tut2.html#105>`_
 
    `methods`:idx:
      `manual.html#200 <manual.html#200>`_
 
    `min`:idx:
-     * `system.html#316 <system.html#316>`_
-     * `system.html#436 <system.html#436>`_
+     * `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>`_
 
    `mod`:idx:
-     * `system.html#221 <system.html#221>`_
      * `system.html#222 <system.html#222>`_
      * `system.html#223 <system.html#223>`_
      * `system.html#224 <system.html#224>`_
      * `system.html#225 <system.html#225>`_
+     * `system.html#226 <system.html#226>`_
 
    `modify_defaults_file`:idx:
      `mysql.html#284 <mysql.html#284>`_
@@ -2360,10 +2463,13 @@ Index
      * `tut1.html#125 <tut1.html#125>`_
 
    `moveFile`:idx:
-     `os.html#140 <os.html#140>`_
+     `os.html#143 <os.html#143>`_
 
    `moveMem`:idx:
-     `system.html#406 <system.html#406>`_
+     `system.html#408 <system.html#408>`_
+
+   `multi-methods`:idx:
+     `tut2.html#104 <tut2.html#104>`_
 
    `MULTIPLE_KEY_FLAG`:idx:
      `mysql.html#127 <mysql.html#127>`_
@@ -2883,13 +2989,16 @@ Index
      `mysql.html#110 <mysql.html#110>`_
 
    `nan`:idx:
-     `system.html#429 <system.html#429>`_
+     `system.html#430 <system.html#430>`_
 
    `Natural`:idx:
-     `system.html#133 <system.html#133>`_
+     `system.html#134 <system.html#134>`_
+
+   `natural`:idx:
+     `pegs.html#130 <pegs.html#130>`_
 
    `neginf`:idx:
-     `system.html#428 <system.html#428>`_
+     `system.html#429 <system.html#429>`_
 
    `nestList`:idx:
      `macros.html#150 <macros.html#150>`_
@@ -2934,8 +3043,8 @@ Index
      `mysql.html#187 <mysql.html#187>`_
 
    `new`:idx:
-     * `system.html#123 <system.html#123>`_
      * `system.html#124 <system.html#124>`_
+     * `system.html#125 <system.html#125>`_
 
    `newCall`:idx:
      * `macros.html#148 <macros.html#148>`_
@@ -2955,8 +3064,12 @@ Index
    `newIntLitNode`:idx:
      `macros.html#140 <macros.html#140>`_
 
+   `newLine`:idx:
+     `pegs.html#120 <pegs.html#120>`_
+
    `newline`:idx:
-     `manual.html#121 <manual.html#121>`_
+     * `manual.html#121 <manual.html#121>`_
+     * `pegs.html#119 <pegs.html#119>`_
 
    `NewLines`:idx:
      `lexbase.html#102 <lexbase.html#102>`_
@@ -2964,11 +3077,14 @@ Index
    `newNimNode`:idx:
      `macros.html#133 <macros.html#133>`_
 
+   `newNonTerminal`:idx:
+     `pegs.html#123 <pegs.html#123>`_
+
    `newSeq`:idx:
-     `system.html#166 <system.html#166>`_
+     `system.html#167 <system.html#167>`_
 
    `newString`:idx:
-     `system.html#403 <system.html#403>`_
+     `system.html#360 <system.html#360>`_
 
    `newStringStream`:idx:
      `streams.html#117 <streams.html#117>`_
@@ -2981,7 +3097,7 @@ Index
      `macros.html#139 <macros.html#139>`_
 
    `next`:idx:
-     * `parseopt.html#104 <parseopt.html#104>`_
+     * `parseopt.html#105 <parseopt.html#105>`_
      * `parsecfg.html#106 <parsecfg.html#106>`_
      * `parsexml.html#122 <parsexml.html#122>`_
 
@@ -2992,19 +3108,16 @@ Index
      `manual.html#170 <manual.html#170>`_
 
    `NimrodMajor`:idx:
-     `system.html#389 <system.html#389>`_
+     `system.html#393 <system.html#393>`_
 
    `NimrodMinor`:idx:
-     `system.html#390 <system.html#390>`_
+     `system.html#394 <system.html#394>`_
 
    `NimrodPatch`:idx:
-     `system.html#391 <system.html#391>`_
+     `system.html#395 <system.html#395>`_
 
    `NimrodVersion`:idx:
-     `system.html#388 <system.html#388>`_
-
-   `nl`:idx:
-     `strutils.html#108 <strutils.html#108>`_
+     `system.html#392 <system.html#392>`_
 
    `noconv`:idx:
      `manual.html#173 <manual.html#173>`_
@@ -3015,8 +3128,11 @@ Index
    `NO_DEFAULT_VALUE_FLAG`:idx:
      `mysql.html#136 <mysql.html#136>`_
 
+   `nonterminal`:idx:
+     `pegs.html#122 <pegs.html#122>`_
+
    `normalize`:idx:
-     `strutils.html#120 <strutils.html#120>`_
+     `strutils.html#118 <strutils.html#118>`_
 
    `noscript`:idx:
      `xmlgen.html#152 <xmlgen.html#152>`_
@@ -3025,15 +3141,15 @@ Index
      `manual.html#226 <manual.html#226>`_
 
    `not`:idx:
-     * `system.html#119 <system.html#119>`_
-     * `system.html#196 <system.html#196>`_
+     * `system.html#120 <system.html#120>`_
      * `system.html#197 <system.html#197>`_
      * `system.html#198 <system.html#198>`_
      * `system.html#199 <system.html#199>`_
      * `system.html#200 <system.html#200>`_
+     * `system.html#201 <system.html#201>`_
 
    `not_in`:idx:
-     `system.html#351 <system.html#351>`_
+     `system.html#352 <system.html#352>`_
 
    `NOT_NULL_FLAG`:idx:
      `mysql.html#124 <mysql.html#124>`_
@@ -3061,8 +3177,8 @@ Index
      `mysql.html#189 <mysql.html#189>`_
 
    `Open`:idx:
-     * `system.html#495 <system.html#495>`_
-     * `system.html#496 <system.html#496>`_
+     * `system.html#497 <system.html#497>`_
+     * `system.html#498 <system.html#498>`_
 
    `open`:idx:
      * `lexbase.html#104 <lexbase.html#104>`_
@@ -3073,11 +3189,11 @@ Index
 
    `openarray`:idx:
      * `tut1.html#119 <tut1.html#119>`_
-     * `system.html#129 <system.html#129>`_
+     * `system.html#130 <system.html#130>`_
 
    `OpenFile`:idx:
-     * `system.html#493 <system.html#493>`_
-     * `system.html#494 <system.html#494>`_
+     * `system.html#495 <system.html#495>`_
+     * `system.html#496 <system.html#496>`_
 
    `operator`:idx:
      `manual.html#139 <manual.html#139>`_
@@ -3092,15 +3208,15 @@ Index
      `xmlgen.html#156 <xmlgen.html#156>`_
 
    `or`:idx:
-     * `system.html#121 <system.html#121>`_
-     * `system.html#241 <system.html#241>`_
+     * `system.html#122 <system.html#122>`_
      * `system.html#242 <system.html#242>`_
      * `system.html#243 <system.html#243>`_
      * `system.html#244 <system.html#244>`_
      * `system.html#245 <system.html#245>`_
+     * `system.html#246 <system.html#246>`_
 
    `ord`:idx:
-     `system.html#175 <system.html#175>`_
+     `system.html#176 <system.html#176>`_
 
    `ordinal`:idx:
      `tut1.html#114 <tut1.html#114>`_
@@ -3115,7 +3231,7 @@ Index
      `os.html#112 <os.html#112>`_
 
    `outputStream`:idx:
-     `osproc.html#113 <osproc.html#113>`_
+     `osproc.html#115 <osproc.html#115>`_
 
    `p`:idx:
      `xmlgen.html#157 <xmlgen.html#157>`_
@@ -3126,6 +3242,9 @@ Index
    `pairs`:idx:
      `strtabs.html#110 <strtabs.html#110>`_
 
+   `parallelReplace`:idx:
+     `pegs.html#144 <pegs.html#144>`_
+
    `param`:idx:
      `xmlgen.html#158 <xmlgen.html#158>`_
 
@@ -3139,19 +3258,25 @@ Index
      `os.html#102 <os.html#102>`_
 
    `parentDir`:idx:
-     `os.html#126 <os.html#126>`_
+     `os.html#127 <os.html#127>`_
 
    `ParseBiggestInt`:idx:
-     `strutils.html#148 <strutils.html#148>`_
+     `strutils.html#146 <strutils.html#146>`_
 
    `parseCmdLine`:idx:
-     `os.html#152 <os.html#152>`_
+     `os.html#157 <os.html#157>`_
 
    `ParseFloat`:idx:
-     `strutils.html#149 <strutils.html#149>`_
+     `strutils.html#147 <strutils.html#147>`_
 
    `ParseInt`:idx:
-     `strutils.html#147 <strutils.html#147>`_
+     `strutils.html#145 <strutils.html#145>`_
+
+   `parsePeg`:idx:
+     `pegs.html#150 <pegs.html#150>`_
+
+   `parseSQL`:idx:
+     `parsesql.html#109 <parsesql.html#109>`_
 
    `PART_KEY_FLAG`:idx:
      `mysql.html#138 <mysql.html#138>`_
@@ -3276,6 +3401,9 @@ Index
    `Pcurl_version_info_data`:idx:
      `libcurl.html#122 <libcurl.html#122>`_
 
+   `peg`:idx:
+     `pegs.html#151 <pegs.html#151>`_
+
    `Pfd_set`:idx:
      `libcurl.html#138 <libcurl.html#138>`_
 
@@ -3283,10 +3411,10 @@ Index
      `streams.html#118 <streams.html#118>`_
 
    `PFloat32`:idx:
-     `system.html#381 <system.html#381>`_
+     `system.html#385 <system.html#385>`_
 
    `PFloat64`:idx:
-     `system.html#382 <system.html#382>`_
+     `system.html#386 <system.html#386>`_
 
    `Pgptr`:idx:
      `mysql.html#104 <mysql.html#104>`_
@@ -3298,10 +3426,10 @@ Index
      `parsexml.html#115 <parsexml.html#115>`_
 
    `PInt32`:idx:
-     `system.html#384 <system.html#384>`_
+     `system.html#388 <system.html#388>`_
 
    `PInt64`:idx:
-     `system.html#383 <system.html#383>`_
+     `system.html#387 <system.html#387>`_
 
    `PIRest`:idx:
      `parsexml.html#116 <parsexml.html#116>`_
@@ -3375,8 +3503,11 @@ Index
    `PNimrodType`:idx:
      `macros.html#108 <macros.html#108>`_
 
+   `PNonTerminal`:idx:
+     `pegs.html#101 <pegs.html#101>`_
+
    `PObject`:idx:
-     `system.html#136 <system.html#136>`_
+     `system.html#137 <system.html#137>`_
 
    `pointer`:idx:
      `system.html#113 <system.html#113>`_
@@ -3386,10 +3517,10 @@ Index
      * `tut1.html#120 <tut1.html#120>`_
 
    `pop`:idx:
-     `system.html#466 <system.html#466>`_
+     `system.html#467 <system.html#467>`_
 
    `Positive`:idx:
-     `system.html#134 <system.html#134>`_
+     `system.html#135 <system.html#135>`_
 
    `pow`:idx:
      `math.html#132 <math.html#132>`_
@@ -3422,7 +3553,7 @@ Index
      `xmlgen.html#159 <xmlgen.html#159>`_
 
    `pred`:idx:
-     `system.html#163 <system.html#163>`_
+     `system.html#164 <system.html#164>`_
 
    `PRI_KEY_FLAG`:idx:
      `mysql.html#125 <mysql.html#125>`_
@@ -3438,7 +3569,7 @@ Index
      `parsecsv.html#107 <parsecsv.html#107>`_
 
    `processID`:idx:
-     `osproc.html#110 <osproc.html#110>`_
+     `osproc.html#112 <osproc.html#112>`_
 
    `Psockaddr`:idx:
      `mysql.html#250 <mysql.html#250>`_
@@ -3455,6 +3586,9 @@ Index
    `Psqlite3_value`:idx:
      `sqlite3.html#180 <sqlite3.html#180>`_
 
+   `PSqlNode`:idx:
+     `parsesql.html#104 <parsesql.html#104>`_
+
    `Pst_dynamic_array`:idx:
      `mysql.html#341 <mysql.html#341>`_
 
@@ -3531,7 +3665,7 @@ Index
      `manual.html#232 <manual.html#232>`_
 
    `putEnv`:idx:
-     `os.html#145 <os.html#145>`_
+     `os.html#149 <os.html#149>`_
 
    `PVIO`:idx:
      `mysql.html#103 <mysql.html#103>`_
@@ -3543,20 +3677,20 @@ Index
      `xmlgen.html#160 <xmlgen.html#160>`_
 
    `quit`:idx:
-     * `system.html#485 <system.html#485>`_
-     * `system.html#486 <system.html#486>`_
+     * `system.html#487 <system.html#487>`_
+     * `system.html#488 <system.html#488>`_
 
    `QuitFailure`:idx:
-     `system.html#484 <system.html#484>`_
+     `system.html#486 <system.html#486>`_
 
    `QuitSuccess`:idx:
-     `system.html#483 <system.html#483>`_
+     `system.html#485 <system.html#485>`_
 
    `quotation mark`:idx:
      `manual.html#128 <manual.html#128>`_
 
    `quoteIfContainsWhite`:idx:
-     `strutils.html#156 <strutils.html#156>`_
+     `strutils.html#154 <strutils.html#154>`_
 
    `random`:idx:
      `math.html#113 <math.html#113>`_
@@ -3571,7 +3705,7 @@ Index
      `mysql.html#254 <mysql.html#254>`_
 
    `range`:idx:
-     `system.html#127 <system.html#127>`_
+     `system.html#128 <system.html#128>`_
 
    `re-raised`:idx:
      `manual.html#186 <manual.html#186>`_
@@ -3580,23 +3714,23 @@ Index
      `streams.html#106 <streams.html#106>`_
 
    `readBuffer`:idx:
-     `system.html#516 <system.html#516>`_
+     `system.html#518 <system.html#518>`_
 
    `ReadBytes`:idx:
-     `system.html#514 <system.html#514>`_
+     `system.html#516 <system.html#516>`_
 
    `readChar`:idx:
-     * `system.html#500 <system.html#500>`_
+     * `system.html#502 <system.html#502>`_
      * `streams.html#105 <streams.html#105>`_
 
    `ReadChars`:idx:
-     `system.html#515 <system.html#515>`_
+     `system.html#517 <system.html#517>`_
 
    `readData`:idx:
      `cgi.html#108 <cgi.html#108>`_
 
    `readFile`:idx:
-     `system.html#502 <system.html#502>`_
+     `system.html#504 <system.html#504>`_
 
    `readFloat32`:idx:
      `streams.html#111 <streams.html#111>`_
@@ -3617,7 +3751,7 @@ Index
      `streams.html#107 <streams.html#107>`_
 
    `readLine`:idx:
-     * `system.html#510 <system.html#510>`_
+     * `system.html#512 <system.html#512>`_
      * `streams.html#114 <streams.html#114>`_
 
    `readRow`:idx:
@@ -3627,19 +3761,19 @@ Index
      `streams.html#113 <streams.html#113>`_
 
    `realloc`:idx:
-     `system.html#410 <system.html#410>`_
+     `system.html#412 <system.html#412>`_
 
    `reBinary`:idx:
-     `regexprs.html#115 <regexprs.html#115>`_
+     `regexprs.html#116 <regexprs.html#116>`_
 
    `Recursive module dependancies`:idx:
      `manual.html#220 <manual.html#220>`_
 
    `reEmail`:idx:
-     `regexprs.html#118 <regexprs.html#118>`_
+     `regexprs.html#119 <regexprs.html#119>`_
 
    `reFloat`:idx:
-     `regexprs.html#117 <regexprs.html#117>`_
+     `regexprs.html#118 <regexprs.html#118>`_
 
    `REFRESH_DES_KEY_FILE`:idx:
      `mysql.html#154 <mysql.html#154>`_
@@ -3687,39 +3821,43 @@ Index
      `nimrodc.html#112 <nimrodc.html#112>`_
 
    `reHex`:idx:
-     `regexprs.html#114 <regexprs.html#114>`_
+     `regexprs.html#115 <regexprs.html#115>`_
 
    `reIdentifier`:idx:
-     `regexprs.html#111 <regexprs.html#111>`_
+     `regexprs.html#112 <regexprs.html#112>`_
 
    `reInteger`:idx:
-     `regexprs.html#113 <regexprs.html#113>`_
+     `regexprs.html#114 <regexprs.html#114>`_
 
    `removeDir`:idx:
-     `os.html#150 <os.html#150>`_
+     `os.html#155 <os.html#155>`_
 
    `removeFile`:idx:
-     `os.html#141 <os.html#141>`_
+     `os.html#144 <os.html#144>`_
 
    `reNatural`:idx:
-     `regexprs.html#112 <regexprs.html#112>`_
+     `regexprs.html#113 <regexprs.html#113>`_
+
+   `renderSQL`:idx:
+     `parsesql.html#110 <parsesql.html#110>`_
 
    `reOctal`:idx:
-     `regexprs.html#116 <regexprs.html#116>`_
+     `regexprs.html#117 <regexprs.html#117>`_
 
    `repeatChar`:idx:
-     `strutils.html#151 <strutils.html#151>`_
+     `strutils.html#149 <strutils.html#149>`_
 
    `replace`:idx:
-     * `strutils.html#159 <strutils.html#159>`_
-     * `strutils.html#160 <strutils.html#160>`_
+     * `strutils.html#157 <strutils.html#157>`_
+     * `strutils.html#158 <strutils.html#158>`_
+     * `pegs.html#143 <pegs.html#143>`_
 
    `replaceStr`:idx:
-     * `strutils.html#127 <strutils.html#127>`_
-     * `strutils.html#128 <strutils.html#128>`_
+     * `strutils.html#125 <strutils.html#125>`_
+     * `strutils.html#126 <strutils.html#126>`_
 
    `repr`:idx:
-     `system.html#366 <system.html#366>`_
+     `system.html#370 <system.html#370>`_
 
    `ResetAttributes`:idx:
      `terminal.html#110 <terminal.html#110>`_
@@ -3729,28 +3867,31 @@ Index
      * `manual.html#204 <manual.html#204>`_
 
    `resume`:idx:
-     `osproc.html#107 <osproc.html#107>`_
+     `osproc.html#109 <osproc.html#109>`_
 
    `return`:idx:
      `manual.html#192 <manual.html#192>`_
 
    `reURL`:idx:
-     `regexprs.html#119 <regexprs.html#119>`_
+     `regexprs.html#120 <regexprs.html#120>`_
 
    `round`:idx:
      `math.html#121 <math.html#121>`_
 
    `runeAt`:idx:
-     `unicode.html#104 <unicode.html#104>`_
+     `unicode.html#109 <unicode.html#109>`_
 
    `runeLen`:idx:
-     `unicode.html#103 <unicode.html#103>`_
+     `unicode.html#106 <unicode.html#106>`_
+
+   `runeLenAt`:idx:
+     `unicode.html#107 <unicode.html#107>`_
 
    `runes`:idx:
-     `unicode.html#114 <unicode.html#114>`_
+     `unicode.html#119 <unicode.html#119>`_
 
    `running`:idx:
-     `osproc.html#109 <osproc.html#109>`_
+     `osproc.html#111 <osproc.html#111>`_
 
    `safe`:idx:
      `manual.html#112 <manual.html#112>`_
@@ -3759,10 +3900,10 @@ Index
      `manual.html#167 <manual.html#167>`_
 
    `sameFile`:idx:
-     `os.html#137 <os.html#137>`_
+     `os.html#140 <os.html#140>`_
 
    `sameFileContent`:idx:
-     `os.html#138 <os.html#138>`_
+     `os.html#141 <os.html#141>`_
 
    `samp`:idx:
      `xmlgen.html#161 <xmlgen.html#161>`_
@@ -3803,7 +3944,10 @@ Index
      * `tut1.html#127 <tut1.html#127>`_
 
    `seq`:idx:
-     `system.html#130 <system.html#130>`_
+     `system.html#131 <system.html#131>`_
+
+   `sequence`:idx:
+     `pegs.html#109 <pegs.html#109>`_
 
    `Sequences`:idx:
      * `manual.html#154 <manual.html#154>`_
@@ -3843,7 +3987,7 @@ Index
      `mysql.html#113 <mysql.html#113>`_
 
    `set`:idx:
-     `system.html#131 <system.html#131>`_
+     `system.html#132 <system.html#132>`_
 
    `set type`:idx:
      * `manual.html#158 <manual.html#158>`_
@@ -3865,10 +4009,10 @@ Index
      `terminal.html#103 <terminal.html#103>`_
 
    `setFilePermissions`:idx:
-     `os.html#155 <os.html#155>`_
+     `os.html#160 <os.html#160>`_
 
    `setFilePos`:idx:
-     `system.html#520 <system.html#520>`_
+     `system.html#522 <system.html#522>`_
 
    `SET_FLAG`:idx:
      `mysql.html#135 <mysql.html#135>`_
@@ -3877,25 +4021,28 @@ Index
      `terminal.html#115 <terminal.html#115>`_
 
    `setLen`:idx:
-     * `system.html#402 <system.html#402>`_
-     * `system.html#412 <system.html#412>`_
+     * `system.html#358 <system.html#358>`_
+     * `system.html#359 <system.html#359>`_
 
    `setTestData`:idx:
      `cgi.html#142 <cgi.html#142>`_
 
    `shl`:idx:
-     * `system.html#231 <system.html#231>`_
      * `system.html#232 <system.html#232>`_
      * `system.html#233 <system.html#233>`_
      * `system.html#234 <system.html#234>`_
      * `system.html#235 <system.html#235>`_
+     * `system.html#236 <system.html#236>`_
 
    `shr`:idx:
-     * `system.html#226 <system.html#226>`_
      * `system.html#227 <system.html#227>`_
      * `system.html#228 <system.html#228>`_
      * `system.html#229 <system.html#229>`_
      * `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>`_
@@ -3904,7 +4051,7 @@ Index
      `math.html#129 <math.html#129>`_
 
    `sizeof`:idx:
-     `system.html#161 <system.html#161>`_
+     `system.html#162 <system.html#162>`_
 
    `small`:idx:
      `xmlgen.html#164 <xmlgen.html#164>`_
@@ -3916,27 +4063,33 @@ Index
      `xmlgen.html#165 <xmlgen.html#165>`_
 
    `split`:idx:
-     * `strutils.html#131 <strutils.html#131>`_
-     * `strutils.html#132 <strutils.html#132>`_
-     * `strutils.html#138 <strutils.html#138>`_
-     * `strutils.html#139 <strutils.html#139>`_
+     * `strutils.html#129 <strutils.html#129>`_
+     * `strutils.html#130 <strutils.html#130>`_
+     * `strutils.html#136 <strutils.html#136>`_
+     * `strutils.html#137 <strutils.html#137>`_
+     * `pegs.html#146 <pegs.html#146>`_
+     * `pegs.html#147 <pegs.html#147>`_
+
+   `splitFile`:idx:
+     `os.html#129 <os.html#129>`_
 
    `SplitFilename`:idx:
-     `os.html#131 <os.html#131>`_
+     `os.html#133 <os.html#133>`_
 
    `splitLines`:idx:
-     * `strutils.html#133 <strutils.html#133>`_
-     * `strutils.html#137 <strutils.html#137>`_
+     * `strutils.html#131 <strutils.html#131>`_
+     * `strutils.html#135 <strutils.html#135>`_
 
    `splitLinesSeq`:idx:
-     `strutils.html#134 <strutils.html#134>`_
+     `strutils.html#132 <strutils.html#132>`_
 
    `SplitPath`:idx:
-     `os.html#125 <os.html#125>`_
+     * `os.html#125 <os.html#125>`_
+     * `os.html#126 <os.html#126>`_
 
    `splitSeq`:idx:
-     * `strutils.html#135 <strutils.html#135>`_
-     * `strutils.html#136 <strutils.html#136>`_
+     * `strutils.html#133 <strutils.html#133>`_
+     * `strutils.html#134 <strutils.html#134>`_
 
    `sqlite3_aggregate_context`:idx:
      `sqlite3.html#261 <sqlite3.html#261>`_
@@ -4447,14 +4600,15 @@ Index
      `math.html#136 <math.html#136>`_
 
    `startProcess`:idx:
-     * `osproc.html#105 <osproc.html#105>`_
-     * `osproc.html#116 <osproc.html#116>`_
+     * `osproc.html#107 <osproc.html#107>`_
+     * `osproc.html#117 <osproc.html#117>`_
 
    `startsWith`:idx:
-     `strutils.html#152 <strutils.html#152>`_
+     * `strutils.html#150 <strutils.html#150>`_
+     * `pegs.html#141 <pegs.html#141>`_
 
    `statement macros`:idx:
-     `tut2.html#111 <tut2.html#111>`_
+     `tut2.html#112 <tut2.html#112>`_
 
    `Statements`:idx:
      `manual.html#176 <manual.html#176>`_
@@ -4469,13 +4623,13 @@ Index
      `manual.html#165 <manual.html#165>`_
 
    `stderr`:idx:
-     `system.html#492 <system.html#492>`_
+     `system.html#494 <system.html#494>`_
 
    `stdin`:idx:
-     `system.html#490 <system.html#490>`_
+     `system.html#492 <system.html#492>`_
 
    `stdout`:idx:
-     `system.html#491 <system.html#491>`_
+     `system.html#493 <system.html#493>`_
 
    `st_dynamic_array`:idx:
      `mysql.html#339 <mysql.html#339>`_
@@ -4527,20 +4681,17 @@ Index
      * `system.html#111 <system.html#111>`_
 
    `string interpolation`:idx:
-     `strutils.html#110 <strutils.html#110>`_
+     `strutils.html#108 <strutils.html#108>`_
 
    `String literals`:idx:
      `manual.html#119 <manual.html#119>`_
 
    `strip`:idx:
-     `strutils.html#114 <strutils.html#114>`_
+     `strutils.html#112 <strutils.html#112>`_
 
    `strong`:idx:
      `xmlgen.html#166 <xmlgen.html#166>`_
 
-   `strStart`:idx:
-     `strutils.html#107 <strutils.html#107>`_
-
    `structured type`:idx:
      `manual.html#152 <manual.html#152>`_
 
@@ -4573,10 +4724,10 @@ Index
      * `tut1.html#115 <tut1.html#115>`_
 
    `substitution`:idx:
-     `strutils.html#109 <strutils.html#109>`_
+     `strutils.html#107 <strutils.html#107>`_
 
    `succ`:idx:
-     `system.html#162 <system.html#162>`_
+     `system.html#163 <system.html#163>`_
 
    `sum`:idx:
      `math.html#110 <math.html#110>`_
@@ -4585,10 +4736,13 @@ Index
      `xmlgen.html#169 <xmlgen.html#169>`_
 
    `suspend`:idx:
-     `osproc.html#106 <osproc.html#106>`_
+     `osproc.html#108 <osproc.html#108>`_
 
    `swap`:idx:
-     `system.html#414 <system.html#414>`_
+     `system.html#415 <system.html#415>`_
+
+   `symAddr`:idx:
+     `dynlib.html#104 <dynlib.html#104>`_
 
    `symbol`:idx:
      `macros.html#123 <macros.html#123>`_
@@ -4609,7 +4763,7 @@ Index
      `manual.html#125 <manual.html#125>`_
 
    `TAddress`:idx:
-     `system.html#367 <system.html#367>`_
+     `system.html#371 <system.html#371>`_
 
    `tan`:idx:
      `math.html#130 <math.html#130>`_
@@ -4822,25 +4976,35 @@ Index
      `manual.html#213 <manual.html#213>`_
 
    `TEndian`:idx:
-     `system.html#380 <system.html#380>`_
+     `system.html#384 <system.html#384>`_
+
+   `term`:idx:
+     * `pegs.html#103 <pegs.html#103>`_
+     * `pegs.html#106 <pegs.html#106>`_
+
+   `termIgnoreCase`:idx:
+     `pegs.html#104 <pegs.html#104>`_
+
+   `termIgnoreStyle`:idx:
+     `pegs.html#105 <pegs.html#105>`_
 
    `terminate`:idx:
-     `osproc.html#108 <osproc.html#108>`_
+     `osproc.html#110 <osproc.html#110>`_
 
    `textarea`:idx:
      `xmlgen.html#173 <xmlgen.html#173>`_
 
    `TFile`:idx:
-     `system.html#487 <system.html#487>`_
+     `system.html#489 <system.html#489>`_
 
    `TFileHandle`:idx:
-     `system.html#489 <system.html#489>`_
+     `system.html#491 <system.html#491>`_
 
    `TFileMode`:idx:
-     `system.html#488 <system.html#488>`_
+     `system.html#490 <system.html#490>`_
 
    `TFilePermission`:idx:
-     `os.html#153 <os.html#153>`_
+     `os.html#158 <os.html#158>`_
 
    `TFileStream`:idx:
      `streams.html#119 <streams.html#119>`_
@@ -4858,7 +5022,7 @@ Index
      `strtabs.html#111 <strtabs.html#111>`_
 
    `TGC_Strategy`:idx:
-     `system.html#471 <system.html#471>`_
+     `system.html#472 <system.html#472>`_
 
    `th`:idx:
      `xmlgen.html#175 <xmlgen.html#175>`_
@@ -4878,6 +5042,9 @@ Index
    `title`:idx:
      `xmlgen.html#177 <xmlgen.html#177>`_
 
+   `TLibHandle`:idx:
+     `dynlib.html#101 <dynlib.html#101>`_
+
    `TMonth`:idx:
      `times.html#101 <times.html#101>`_
 
@@ -4900,68 +5067,71 @@ Index
      `macros.html#105 <macros.html#105>`_
 
    `toBiggestFloat`:idx:
-     `system.html#396 <system.html#396>`_
+     `system.html#400 <system.html#400>`_
 
    `toBiggestInt`:idx:
-     `system.html#398 <system.html#398>`_
+     `system.html#402 <system.html#402>`_
 
    `toBin`:idx:
-     `strutils.html#164 <strutils.html#164>`_
+     `strutils.html#162 <strutils.html#162>`_
 
    `TObject`:idx:
-     `system.html#135 <system.html#135>`_
+     `system.html#136 <system.html#136>`_
 
    `toFloat`:idx:
-     `system.html#395 <system.html#395>`_
+     `system.html#399 <system.html#399>`_
 
    `toHex`:idx:
-     `strutils.html#145 <strutils.html#145>`_
+     `strutils.html#143 <strutils.html#143>`_
 
    `toInt`:idx:
-     `system.html#397 <system.html#397>`_
+     `system.html#401 <system.html#401>`_
 
    `toLower`:idx:
-     * `strutils.html#115 <strutils.html#115>`_
-     * `strutils.html#116 <strutils.html#116>`_
-     * `unicode.html#106 <unicode.html#106>`_
+     * `strutils.html#113 <strutils.html#113>`_
+     * `strutils.html#114 <strutils.html#114>`_
+     * `unicode.html#111 <unicode.html#111>`_
 
    `toOct`:idx:
-     `strutils.html#163 <strutils.html#163>`_
+     `strutils.html#161 <strutils.html#161>`_
 
    `toOctal`:idx:
-     `strutils.html#130 <strutils.html#130>`_
+     `strutils.html#128 <strutils.html#128>`_
 
    `TOptParser`:idx:
      `parseopt.html#102 <parseopt.html#102>`_
 
    `toString`:idx:
-     `strutils.html#150 <strutils.html#150>`_
+     `strutils.html#148 <strutils.html#148>`_
 
    `toStrLit`:idx:
      `macros.html#144 <macros.html#144>`_
 
    `toTitle`:idx:
-     `unicode.html#108 <unicode.html#108>`_
+     `unicode.html#113 <unicode.html#113>`_
 
    `toU16`:idx:
-     `system.html#184 <system.html#184>`_
+     `system.html#185 <system.html#185>`_
 
    `toU32`:idx:
-     `system.html#185 <system.html#185>`_
+     `system.html#186 <system.html#186>`_
 
    `toU8`:idx:
-     `system.html#183 <system.html#183>`_
+     `system.html#184 <system.html#184>`_
 
    `toUpper`:idx:
-     * `strutils.html#117 <strutils.html#117>`_
-     * `strutils.html#118 <strutils.html#118>`_
-     * `unicode.html#107 <unicode.html#107>`_
+     * `strutils.html#115 <strutils.html#115>`_
+     * `strutils.html#116 <strutils.html#116>`_
+     * `unicode.html#112 <unicode.html#112>`_
 
    `toUTF8`:idx:
-     `unicode.html#105 <unicode.html#105>`_
+     `unicode.html#110 <unicode.html#110>`_
 
    `TPathComponent`:idx:
-     `os.html#148 <os.html#148>`_
+     `os.html#153 <os.html#153>`_
+
+   `TPeg`:idx:
+     `pegs.html#102 <pegs.html#102>`_
 
    `TProcessOption`:idx:
      `osproc.html#102 <osproc.html#102>`_
@@ -4973,11 +5143,14 @@ Index
      * `manual.html#160 <manual.html#160>`_
      * `tut1.html#121 <tut1.html#121>`_
 
+   `transformFile`:idx:
+     `pegs.html#145 <pegs.html#145>`_
+
    `TRequestMethod`:idx:
      `cgi.html#105 <cgi.html#105>`_
 
    `TResult`:idx:
-     `system.html#160 <system.html#160>`_
+     `system.html#161 <system.html#161>`_
 
    `TRune`:idx:
      `unicode.html#101 <unicode.html#101>`_
@@ -4990,7 +5163,7 @@ Index
 
    `try`:idx:
      * `manual.html#188 <manual.html#188>`_
-     * `tut2.html#107 <tut2.html#107>`_
+     * `tut2.html#108 <tut2.html#108>`_
 
    `Tsqlite3_callback`:idx:
      `sqlite3.html#182 <sqlite3.html#182>`_
@@ -5004,6 +5177,18 @@ Index
    `Tsqlite3_result_func`:idx:
      `sqlite3.html#187 <sqlite3.html#187>`_
 
+   `TSqlLexer`:idx:
+     `parsesql.html#101 <parsesql.html#101>`_
+
+   `TSqlNode`:idx:
+     `parsesql.html#105 <parsesql.html#105>`_
+
+   `TSqlNodeKind`:idx:
+     `parsesql.html#102 <parsesql.html#102>`_
+
+   `TSqlParser`:idx:
+     `parsesql.html#106 <parsesql.html#106>`_
+
    `TStream`:idx:
      `streams.html#102 <streams.html#102>`_
 
@@ -5068,7 +5253,7 @@ Index
 
    `type parameters`:idx:
      * `manual.html#212 <manual.html#212>`_
-     * `tut2.html#109 <tut2.html#109>`_
+     * `tut2.html#110 <tut2.html#110>`_
 
    `type suffix`:idx:
      `manual.html#138 <manual.html#138>`_
@@ -5106,6 +5291,9 @@ Index
    `UnixToNativePath`:idx:
      `os.html#113 <os.html#113>`_
 
+   `UnloadLib`:idx:
+     `dynlib.html#103 <dynlib.html#103>`_
+
    `unsigned integer`:idx:
      * `manual.html#143 <manual.html#143>`_
      * `tut1.html#108 <tut1.html#108>`_
@@ -5143,17 +5331,17 @@ Index
      `cgi.html#109 <cgi.html#109>`_
 
    `validEmailAddress`:idx:
-     `strutils.html#166 <strutils.html#166>`_
+     `strutils.html#164 <strutils.html#164>`_
 
    `validIdentifier`:idx:
-     `strutils.html#167 <strutils.html#167>`_
-
-   `Var`:idx:
-     `manual.html#181 <manual.html#181>`_
+     `strutils.html#165 <strutils.html#165>`_
 
    `var`:idx:
      `xmlgen.html#181 <xmlgen.html#181>`_
 
+   `Var`:idx:
+     `manual.html#181 <manual.html#181>`_
+
    `varargs`:idx:
      `nimrodc.html#106 <nimrodc.html#106>`_
 
@@ -5166,7 +5354,7 @@ Index
      * `tut2.html#103 <tut2.html#103>`_
 
    `verbose`:idx:
-     `regexprs.html#120 <regexprs.html#120>`_
+     `regexprs.html#121 <regexprs.html#121>`_
 
    `vertical tabulator`:idx:
      `manual.html#126 <manual.html#126>`_
@@ -5175,13 +5363,13 @@ Index
      `nimrodc.html#111 <nimrodc.html#111>`_
 
    `waitForExit`:idx:
-     `osproc.html#111 <osproc.html#111>`_
+     `osproc.html#113 <osproc.html#113>`_
 
    `walkDir`:idx:
-     `os.html#149 <os.html#149>`_
+     `os.html#154 <os.html#154>`_
 
    `walkFiles`:idx:
-     * `os.html#147 <os.html#147>`_
+     * `os.html#152 <os.html#152>`_
      * `zipfiles.html#110 <zipfiles.html#110>`_
 
    `warning`:idx:
@@ -5196,6 +5384,9 @@ 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>`_
 
@@ -5203,31 +5394,31 @@ Index
      `times.html#118 <times.html#118>`_
 
    `write`:idx:
-     * `system.html#503 <system.html#503>`_
-     * `system.html#504 <system.html#504>`_
      * `system.html#505 <system.html#505>`_
      * `system.html#506 <system.html#506>`_
      * `system.html#507 <system.html#507>`_
      * `system.html#508 <system.html#508>`_
      * `system.html#509 <system.html#509>`_
+     * `system.html#510 <system.html#510>`_
+     * `system.html#511 <system.html#511>`_
      * `streams.html#103 <streams.html#103>`_
      * `streams.html#104 <streams.html#104>`_
 
    `writeBuffer`:idx:
-     `system.html#519 <system.html#519>`_
+     `system.html#521 <system.html#521>`_
 
    `writeBytes`:idx:
-     `system.html#517 <system.html#517>`_
+     `system.html#519 <system.html#519>`_
 
    `writeChars`:idx:
-     `system.html#518 <system.html#518>`_
+     `system.html#520 <system.html#520>`_
 
    `writeContentType`:idx:
      `cgi.html#143 <cgi.html#143>`_
 
    `writeln`:idx:
-     * `system.html#511 <system.html#511>`_
-     * `system.html#512 <system.html#512>`_
+     * `system.html#513 <system.html#513>`_
+     * `system.html#514 <system.html#514>`_
 
    `WriteStyled`:idx:
      `terminal.html#112 <terminal.html#112>`_
@@ -5243,28 +5434,28 @@ Index
      `cgi.html#103 <cgi.html#103>`_
 
    `xor`:idx:
-     * `system.html#122 <system.html#122>`_
-     * `system.html#246 <system.html#246>`_
+     * `system.html#123 <system.html#123>`_
      * `system.html#247 <system.html#247>`_
      * `system.html#248 <system.html#248>`_
      * `system.html#249 <system.html#249>`_
      * `system.html#250 <system.html#250>`_
+     * `system.html#251 <system.html#251>`_
 
    `yield`:idx:
      `manual.html#194 <manual.html#194>`_
 
    `ze`:idx:
-     * `system.html#177 <system.html#177>`_
      * `system.html#178 <system.html#178>`_
+     * `system.html#179 <system.html#179>`_
 
    `ze64`:idx:
-     * `system.html#179 <system.html#179>`_
      * `system.html#180 <system.html#180>`_
      * `system.html#181 <system.html#181>`_
      * `system.html#182 <system.html#182>`_
+     * `system.html#183 <system.html#183>`_
 
    `ZEROFILL_FLAG`:idx:
      `mysql.html#130 <mysql.html#130>`_
 
    `zeroMem`:idx:
-     `system.html#404 <system.html#404>`_
\ No newline at end of file
+     `system.html#406 <system.html#406>`_
\ No newline at end of file
diff --git a/doc/tut1.txt b/doc/tut1.txt
index bf744b885..69f218a31 100755
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -1,6 +1,6 @@
-============================
-The Nimrod Tutorial (Part I)
-============================
+========================
+Nimrod Tutorial (Part I)
+========================
 
 :Author: Andreas Rumpf
 :Version: |nimrodversion|
@@ -63,7 +63,8 @@ Nimrod: It is a good compromise between brevity and readability.
 
 The "hallo world" program contains several identifiers that are already
 known to the compiler: ``echo``, ``readLine``, etc. These built-in items are
-declared in the system_ module which is implicitly imported by any other module.
+declared in the system_ module which is implicitly imported by any other
+module.
 
 
 Lexical elements
@@ -173,7 +174,7 @@ to a storage location:
   var x = "abc" # introduces a new variable `x` and assigns a value to it
   x = "xyz"     # assigns a new value to `x`
 
-The ``=`` is called the *assignment operator*. The assignment operator cannot
+``=`` is the *assignment operator*. The assignment operator cannot
 be overloaded, overwritten or forbidden, but this might change in a future
 version of Nimrod.
 
@@ -196,7 +197,7 @@ constants:
     x = 1
     # a comment can occur here too
     y = 2
-    z = y + 5 # simple computations are possible
+    z = y + 5 # computations are possible
 
 
 Control flow statements
@@ -277,14 +278,13 @@ the compiler that for every other value nothing should be done:
 The ``nil`` statement is a *do nothing* statement. The compiler knows that a
 case statement with an else part cannot fail and thus the error disappers. Note
 that it is impossible to cover any possible string value: That is why there is
-no such compiler check for string cases.
+no such check for string cases.
 
 In general the case statement is used for subrange types or enumerations where
 it is of great help that the compiler checks that you covered any possible
 value.
 
 
-
 While statement
 ---------------
 
@@ -327,7 +327,7 @@ the same:
     Echo($i)
     inc(i) # increment i by 1
 
-Counting down can be achieved as easily (but is much less needed):
+Counting down can be achieved as easily (but is less often needed):
 
 .. code-block:: nimrod
   Echo("Counting down from 10 to 1: ")
@@ -372,7 +372,7 @@ Break statement
 ---------------
 A block can be left prematurely with a ``break`` statement. The break statement
 can leave a while, for, or a block statement. It leaves the innermost construct,
-unless the label of a block is given:
+unless a label of a block is given:
 
 .. code-block:: nimrod
   block myblock:
@@ -392,7 +392,7 @@ unless the label of a block is given:
 
 Continue statement
 ------------------
-Like in many other programming languages, a ``continue`` statement leads to
+Like in many other programming languages, a ``continue`` statement starts
 the next iteration immediately:
 
 .. code-block:: nimrod
@@ -512,7 +512,7 @@ the procedure (and therefore the while loop) immediately. The
 parameter named ``question`` of type ``string`` and returns a value of type
 ``bool``. ``Bool`` is a built-in type: The only valid values for ``bool`` are 
 ``true`` and ``false``.
-The conditions in if or while statements need to have the type ``bool``.
+The conditions in if or while statements should be of the type ``bool``.
 
 Some terminology: In the example ``question`` is called a (formal) *parameter*,
 ``"Should I..."`` is called an *argument* that is passed to this parameter.
@@ -614,7 +614,7 @@ Now the call to ``createWindow`` only needs to set the values that differ
 from the defaults.
 
 Note that type inference works for parameters with default values, there is
-no need to specify ``title: string = "unknown"``, for example.
+no need to write ``title: string = "unknown"``, for example.
 
 
 Overloaded procedures
@@ -857,7 +857,6 @@ loses information, the `EOutOfRange`:idx: exception is raised (if the error
 cannot be detected at compile time).
 
 
-
 Floats
 ------
 Nimrod has these floating point types built-in: ``float float32 float64``.
@@ -1232,7 +1231,7 @@ mysterious crashes.
 **Note**: The example only works because the memory is initialized with zero
 (``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
 ``nil`` which the string assignment can handle. You need to know low level
-details like this when mixing garbage collected data with unmanaged memory!
+details like this when mixing garbage collected data with unmanaged memory.
 
 
 Procedural type
@@ -1240,8 +1239,7 @@ Procedural type
 A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure. 
 ``nil`` is an allowed value for a variable 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 (details follow in the OOP section).
+techniques.
 
 Example:
 
@@ -1303,9 +1301,9 @@ because then one module cannot be reused without the other.
 
 The algorithm for compiling modules is:
 
-- Compile the whole module as usual, following import statements recursively
-- if there is a cycle only import the already parsed symbols (that are
-  exported); if an unknown identifier occurs then abort
+- Compile the whole module as usual, following import statements recursively.
+- If there is a cycle only import the already parsed symbols (that are
+  exported); if an unknown identifier occurs then abort.
 
 This is best illustrated by an example:
 
diff --git a/doc/tut2.txt b/doc/tut2.txt
index cc67c3400..4515ffbd5 100755
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -1,6 +1,6 @@
-=============================
-The Nimrod Tutorial (Part II)
-=============================
+=========================
+Nimrod Tutorial (Part II)
+=========================
 
 :Author: Andreas Rumpf
 :Version: |nimrodversion|
@@ -73,9 +73,9 @@ section.
 
 Inheritance is done with the ``object of`` syntax. Multiple inheritance is
 currently not supported. If an object type has no suitable ancestor, ``TObject``
-should be used as its ancestor, but this is only a convention.
+can be used as its ancestor, but this is only a convention.
 
-Note that aggregation (*has-a* relation) is often preferable to inheritance
+**Note**: Aggregation (*has-a* relation) is often preferable to inheritance
 (*is-a* relation) for simple code reuse. Since objects are value types in
 Nimrod, aggregation is as efficient as inheritance.
 
@@ -84,9 +84,9 @@ Mutually recursive types
 ------------------------
 
 Objects, tuples and references can model quite complex data structures which
-depend on each other. This is called *mutually recursive types*. In Nimrod 
-these types need to be declared within a single type section. Anything else 
-would require arbitrary symbol lookahead which slows down compilation.
+depend on each other; they are *mutually recursive*. In Nimrod 
+these types can only be declared within a single type section. (Anything else 
+would require arbitrary symbol lookahead which slows down compilation.)
 
 Example:
 
@@ -147,7 +147,7 @@ An example:
     TNode = object
       case kind: TNodeKind  # the ``kind`` field is the discriminator
       of nkInt: intVal: int
-      of nkFloat: floavVal: float
+      of nkFloat: floatVal: float
       of nkString: strVal: string
       of nkAdd, nkSub:
         leftOp, rightOp: PNode
@@ -176,18 +176,25 @@ 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 procedure should belong to: Is
+* 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?
 
-Nimrod avoids these problems by not distinguishing between methods and 
-procedures. Methods are just ordinary procedures. However, there is a special 
-syntactic sugar for calling procedures: The syntax ``obj.method(args)`` can be
-used instead of ``method(obj, args)``. If there are no remaining arguments, the
-parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``).
+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
+distinguished from procs only for dynamic binding purposes.
+
+
+Method call syntax
+------------------
+
+There is a syntactic sugar for calling routines:
+The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``.
+If there are no remaining arguments, the parentheses can be omitted:
+``obj.len`` (instead of ``len(obj)``).
 
 This `method call syntax`:idx: is not restricted to objects, it can be used 
-for any type: 
+for any type:
 
 .. code-block:: nimrod
   
@@ -196,9 +203,17 @@ for any type:
   echo({'a', 'b', 'c'}.card)
   stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
 
-If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
-``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
-that's not true. :-)
+(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:
+
+.. code-block:: nimrod
+  import strutils
+  
+  stdout.writeln("Give a list of numbers (separated by spaces): ")
+  stdout.write(stdin.readLine.split.each(parseInt).max.`$`)
+  stdout.writeln(" is the maximum!")
 
 
 Properties
@@ -261,60 +276,75 @@ already provides ``v[]`` access.
 
 Dynamic dispatch
 ----------------
-In Nimrod procedural types are used to implement dynamic dispatch. The
-following example also shows some more conventions: The ``self`` or ``this``
-object is named ``my`` (because it is shorter than the alternatives), each
-class provides a constructor, etc.
+
+Procedures always use static dispatch. To get dynamic dispatch, replace the
+``proc`` keyword by ``method``:
 
 .. code-block:: nimrod
   type
-    TFigure = object of TObject     # abstract base class:
-      fDraw: proc (my: var TFigure) # concrete classes implement this proc
-    
-  proc init(f: var TFigure) = 
-    f.fDraw = nil
+    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!"
   
-  proc draw(f: var TFigure) =
-    # ``draw`` dispatches dynamically:
-    f.fDraw(f)
+  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)
   
-  type
-    TCircle = object of TFigure
-      radius: int
+  proc newLit(x: int): ref TLiteral =
+    new(result)
+    result.x = x
     
-  proc drawCircle(my: var TCircle) = echo("o " & $my.radius)
+  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)))
   
-  proc init(my: var TCircle) = 
-    init(TFigure(my)) # call base constructor
-    my.radius = 5
-    my.fdraw = drawCircle
+Note that 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
-    TRectangle = object of TFigure
-      width, height: int
+    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"
   
-  proc drawRectangle(my: var TRectangle) = echo("[]")
+  method collide(a: TUnit, b: TThing) {.inline.} =
+    echo "2"
   
-  proc init(my: var TRectangle) = 
-    init(TFigure(my)) # call base constructor
-    my.width = 5
-    my.height = 10
-    my.fdraw = drawRectangle
-
-  # now use these classes:
   var
-    r: TRectangle
-    c: TCircle
-  init(r)
-  init(c)
-  r.draw()
-  c.draw() 
+    a, b: TUnit
+  collide(a, b) # output: 2
+
+
+As the example demonstrates, 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``.
 
-The code uses a ``draw`` procedure that is bound statically, but inside it
-the dynamic dispatch happens with the help of the ``fdraw`` field. Even though
-this solution has its advantages compared to traditional OOP-languages, it is
-a **preliminary** solution. Multimethods are a planned language feature that
-provide a more flexible and efficient mechanism. 
+**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.
 
 
 Exceptions
@@ -497,8 +527,7 @@ simple proc for logging:
     debug = True
     
   proc log(msg: string) {.inline.} = 
-    if debug:
-      stdout.writeln(msg)
+    if debug: stdout.writeln(msg)
   
   var
     x = 4
@@ -506,7 +535,7 @@ simple proc for logging:
 
 This code has a shortcoming: If ``debug`` is set to false someday, the quite
 expensive ``$`` and ``&`` operations are still performed! (The argument 
-evaluation for procedures is said to be *eager*).
+evaluation for procedures is *eager*).
 
 Turning the ``log`` proc into a template solves this problem:
 
@@ -514,21 +543,20 @@ Turning the ``log`` proc into a template solves this problem:
   const
     debug = True
     
-  template log(msg: expr): stmt = 
-    if debug:
-      stdout.writeln(msg)
+  template log(msg: string) = 
+    if debug: stdout.writeln(msg)
   
   var
     x = 4
   log("x has the value: " & $x)
 
-The "types" of templates can be the symbols ``expr`` (stands for *expression*), 
-``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type 
-description*). These are no real types, they just help the compiler parsing.
-However, real types are supported too.
+The parameters' types can be ordinary types or the meta types ``expr``
+(stands for *expression*), ``stmt`` (stands for *statement*) or ``typedesc``
+(stands for *type description*). If the template has no explicit return type,
+``stmt`` is used for consistency with procs and methods.
 
-The template body does not open a new scope. To open a new scope
-use a ``block`` statement:
+The template body does not open a new scope. To open a new scope use a ``block``
+statement:
 
 .. code-block:: nimrod
   template declareInScope(x: expr, t: typeDesc): stmt = 
@@ -573,7 +601,7 @@ In the example the two ``writeln`` statements are bound to the ``actions``
 parameter. The ``withFile`` template contains boilerplate code and helps to
 avoid a common bug: To forget to close the file. Note how the
 ``var fn = filename`` statement ensures that ``filename`` is evaluated only
-once. 
+once.
 
 
 Macros
diff --git a/lib/pure/macros.nim b/lib/pure/macros.nim
index 5568e0da5..341a7cb61 100755
--- a/lib/pure/macros.nim
+++ b/lib/pure/macros.nim
@@ -51,20 +51,21 @@ type
     nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, 
     nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, 
     nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams, 
-    nnkOfInherit, nnkModule, nnkProcDef, 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
+    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, 
@@ -81,9 +82,10 @@ type
   TNimrodSymKind* = enum
     nskUnknown, nskConditional, nskDynLib, nskParam, 
     nskGenericParam, nskTemp, nskType, nskConst, 
-    nskVar, nskProc, nskIterator, nskConverter, 
-    nskMacro, nskTemplate, nskField, nskEnumField, 
-    nskForVar, nskModule, nskLabel, nskStub
+    nskVar, nskProc, nskMethod, nskIterator, 
+    nskConverter, nskMacro, nskTemplate, nskField, 
+    nskEnumField, nskForVar, nskModule, nskLabel, 
+    nskStub
   TNimSymKinds* = set[TNimrodSymKind]
 #[[[end]]]

 

diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 4173cfcce..83cdbc0dd 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -363,7 +363,24 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
   ## The same as ``joinPath(head, tail)``
   return joinPath(head, tail)
 
-proc SplitPath*(path: string, head, tail: var string) {.noSideEffect.} =
+proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
+                                                        deprecated.} =
+  ## **Deprecated since version 0.8.2**: use the version that returns a tuple
+  ## instead
+  var
+    sepPos = -1
+  for i in countdown(len(path)-1, 0):
+    if path[i] in {dirsep, altsep}:
+      sepPos = i
+      break
+  if sepPos >= 0:
+    head = copy(path, 0, sepPos-1)
+    tail = copy(path, sepPos+1)
+  else:
+    head = ""
+    tail = path # make a string copy here
+
+proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
   ## Splits a directory into (head, tail), so that
   ## ``JoinPath(head, tail) == path``.
   ##
@@ -378,11 +395,11 @@ proc SplitPath*(path: string, head, tail: var string) {.noSideEffect.} =
       sepPos = i
       break
   if sepPos >= 0:
-    head = copy(path, 0, sepPos-1)
-    tail = copy(path, sepPos+1)
+    result.head = copy(path, 0, sepPos-1)
+    result.tail = copy(path, sepPos+1)
   else:
-    head = ""
-    tail = path # make a string copy here
+    result.head = ""
+    result.tail = path
 
 proc parentDir*(path: string): string {.noSideEffect.} =
   ## Returns the parent directory of `path`.
@@ -422,25 +439,50 @@ proc searchExtPos(s: string): int =
     elif s[i] in {dirsep, altsep}:
       break # do not skip over path
 
-proc extractDir*(path: string): string {.noSideEffect.} =
+proc splitFile*(path: string): tuple[dir, name, ext: string] {.noSideEffect.} =
+  ## Splits a filename into (dir, filename, extension).
+  ## `dir` does not end in `DirSep`.
+  ## `extension` includes the leading dot.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nimrod
+  ## var (dir, name, ext) = splitFile("usr/local/nimrodc.html")
+  ## assert dir == "usr/local"
+  ## assert name == "nimrodc"
+  ## assert ext == ".html"
+  ## If `path` has no extension, `ext` is the empty string.
+  ## If `path` has no directory component, `dir` is the empty string.
+  ## If `path` has no filename component, `name` and `ext` are empty strings.
+  if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
+    result = (path, "", "")
+  else:
+    var sepPos = -1
+    var dotPos = path.len
+    for i in countdown(len(path)-1, 0):
+      if path[i] == ExtSep:
+        if dotPos == path.len: dotPos = i
+      elif path[i] in {dirsep, altsep}:
+        sepPos = i
+        break
+    result.dir = copy(path, 0, sepPos-1)
+    result.name = copy(path, sepPos+1, dotPos-1)
+    result.ext = copy(path, dotPos)
+
+proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
   ## Extracts the directory of a given path. This is almost the
   ## same as the `head` result of `splitPath`, except that
   ## ``extractDir("/usr/lib/") == "/usr/lib/"``.
-  if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
-    result = path
-  else:
-    var tail: string
-    splitPath(path, result, tail)
+  ## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
+  result = splitFile(path).dir
 
 proc extractFilename*(path: string): string {.noSideEffect.} =
-  ## Extracts the filename of a given `path`. This is almost the
-  ## same as the `tail` result of `splitPath`, except that
-  ## ``extractFilename("/usr/lib/") == ""``.
+  ## Extracts the filename of a given `path`. This is the same as 
+  ## ``name & ext`` from ``splitFile(path)``.
   if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
     result = ""
   else:
-    var head: string
-    splitPath(path, head, result)
+    result = splitPath(path).tail
 
 proc expandFilename*(filename: string): string =
   ## Returns the full path of `filename`, raises EOS in case of an error.
@@ -457,13 +499,14 @@ proc expandFilename*(filename: string): string =
     c_free(res)
 
 proc SplitFilename*(filename: string, name, extension: var string) {.
-  noSideEffect.} = 
+  noSideEffect, deprecated.} = 
   ## Splits a filename into (name, extension), so that
   ## ``name & extension == filename``.
   ##
   ## Example: After ``SplitFilename("usr/local/nimrodc.html", name, ext)``,
   ## `name` is "usr/local/nimrodc" and `ext` is ".html".
-  ## It the file has no extension, extention is the empty string.
+  ## If the file has no extension, extension is the empty string.
+  ## **Deprecated since version 0.8.2**: Use ``splitFile(filename)`` instead.
   var extPos = searchExtPos(filename)
   if extPos >= 0:
     name = copy(filename, 0, extPos-1)
@@ -472,18 +515,19 @@ proc SplitFilename*(filename: string, name, extension: var string) {.
     name = filename # make a string copy here
     extension = ""
 
-proc extractFileExt*(filename: string): string {.noSideEffect.} =
+proc extractFileExt*(filename: string): string {.noSideEffect, deprecated.} =
   ## Extracts the file extension of a given `filename`. This is the
   ## same as the `extension` result of `splitFilename`.
-  var dummy: string
-  splitFilename(filename, dummy, result)
+  ## **Deprecated since version 0.8.2**: Use ``splitFile(filename).ext``
+  ## instead.
+  result = splitFile(filename).ext
 
-proc extractFileTrunk*(filename: string): string {.noSideEffect.} =
+proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
   ## Extracts the file name of a given `filename`. This removes any
   ## directory information and the file extension.
-  var dummy: string
-  splitFilename(extractFilename(filename), result, dummy)
-
+  ## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
+  result = splitFile(filename).name
+  
 proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
   ## Changes the file extension to `ext`.
   ##
@@ -496,8 +540,8 @@ proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
   if extPos < 0: result = filename & normExt(ext)
   else: result = copy(filename, 0, extPos-1) & normExt(ext)
 
-proc AppendFileExt*(filename, ext: string): string {.noSideEffect.} =
-  ## Appends the file extension `ext` to `filename`, unless
+proc addFileExt*(filename, ext: string): string {.noSideEffect.} =
+  ## Adds the file extension `ext` to `filename`, unless
   ## `filename` already has an extension.
   ##
   ## `Ext` should be given without the leading '.', because some
@@ -505,7 +549,12 @@ proc AppendFileExt*(filename, ext: string): string {.noSideEffect.} =
   ## (Although I know of none such beast.)
   var extPos = searchExtPos(filename)
   if extPos < 0: result = filename & normExt(ext)
-  else: result = filename #make a string copy here
+  else: result = filename
+
+proc AppendFileExt*(filename, ext: string): string {.
+  noSideEffect, deprecated.} =
+  ## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
+  result = addFileExt(filename, ext)
 
 proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
   ## Compares two paths.
@@ -610,14 +659,18 @@ proc removeFile*(file: string) =
   ## Removes the `file`. If this fails, `EOS` is raised.
   if cremove(file) != 0'i32: OSError()
 
-proc executeShellCommand*(command: string): int =
+proc executeShellCommand*(command: string): int {.deprecated.} =
+  ## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
+  result = csystem(command)
+
+proc execShellCmd*(command: string): int =
   ## Executes a shell command.
   ##
   ## Command has the form 'program args' where args are the command
   ## line arguments given to program. The proc returns the error code
   ## of the shell when it has finished. The proc does not return until
   ## the process has finished. To execute a program without having a
-  ## shell involved, use the `executeProcess` proc of the `osproc`
+  ## shell involved, use the `execProcess` proc of the `osproc`
   ## module.
   result = csystem(command)
 
@@ -707,7 +760,16 @@ proc putEnv*(key, val: string) =
     if SetEnvironmentVariableA(key, val) == 0'i32:
       OSError()
 
-iterator iterOverEnvironment*(): tuple[key, value: string] =
+iterator iterOverEnvironment*(): tuple[key, value: string] {.deprecated.} =
+  ## Iterate over all environments variables. In the first component of the
+  ## tuple is the name of the current variable stored, in the second its value.
+  ## **Deprecated since version 0.8.2**: Use `envPairs` instead.
+  getEnvVarsC()
+  for i in 0..high(environment):
+    var p = find(environment[i], '=')
+    yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))
+
+iterator envPairs*(): tuple[key, value: string] =
   ## Iterate over all environments variables. In the first component of the
   ## tuple is the name of the current variable stored, in the second its value.
   getEnvVarsC()
@@ -1039,7 +1101,6 @@ proc getApplicationFilename*(): string =
 
 proc getApplicationDir*(): string =
   ## Returns the directory of the application's executable.
-  var tail: string
-  splitPath(getApplicationFilename(), result, tail)
+  result = splitFile(getApplicationFilename()).dir
 
 {.pop.}
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index cf4d926b6..7d017616e 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -36,16 +36,28 @@ type
                          ## often creates a security whole!
     poStdErrToStdOut     ## merge stdout and stderr to the stdout stream
 
-proc executeProcess*(command: string,
-                     options: set[TProcessOption] = {poStdErrToStdOut,
-                                                     poUseShell}): string
+proc execProcess*(command: string,
+                  options: set[TProcessOption] = {poStdErrToStdOut,
+                                                  poUseShell}): string
   ## A convience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
 
-proc executeCommand*(command: string): int
+proc executeProcess*(command: string,
+                     options: set[TProcessOption] = {poStdErrToStdOut,
+                                                     poUseShell}): string {.
+                                                     deprecated.} =
+  ## **Deprecated since version 0.8.2**: Use `execProcess` instead.
+  result = execProcess(command, options)
+
+proc execCmd*(command: string): int
   ## Executes ``command`` and returns its error code. Standard input, output,
   ## error streams are inherited from the calling process.
 
+proc executeCommand*(command: string): int {.deprecated.} =
+  ## **Deprecated since version 0.8.2**: Use `execCmd` instead.
+  result = execCmd(command)
+  
+
 proc startProcess*(command: string,
                    workingDir: string = "",
                    args: openarray[string] = [],
@@ -56,11 +68,12 @@ proc startProcess*(command: string,
   ## is used. `args` are the command line arguments that are passed to the
   ## process. On many operating systems, the first command line argument is the
   ## name of the executable. `args` should not contain this argument!
-  ## `startProcess` takes care of that. `env` is the environment that will be
-  ## passed to the process. If ``env == nil`` the environment is inherited of
+  ## `env` is the environment that will be passed to the process.
+  ## If ``env == nil`` the environment is inherited of
   ## the parent process. `options` are additional flags that may be passed
   ## to `startProcess`. See the documentation of ``TProcessOption`` for the
   ## meaning of these flags.
+  ##
   ## Return value: The newly created process object. Nil is never returned,
   ## but ``EOS`` is raised in case of an error.
 
@@ -104,9 +117,9 @@ proc outputStream*(p: PProcess): PStream
 proc errorStream*(p: PProcess): PStream
   ## returns ``p``'s output stream for reading from
 
-proc executeProcess*(command: string,
-                     options: set[TProcessOption] = {poStdErrToStdOut,
-                                                     poUseShell}): string =
+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])
@@ -208,24 +221,24 @@ when defined(Windows):
     SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
     CreatePipeHandles(SI.hStdInput, HI)
     CreatePipeHandles(HO, Si.hStdOutput)
-    #SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE())
-    #SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE())
     if poStdErrToStdOut in options:
       SI.hStdError = SI.hStdOutput
       HE = HO
     else:
       CreatePipeHandles(HE, Si.hStdError)
-      #SI.hStdError = GetStdHandle(STD_ERROR_HANDLE())
-    #result.inputHandle = open_osfhandle(HI, O_WRONLY)
-    #if result.inputHandle == -1'i32: OSError()
     result.inputHandle = hi
     result.outputHandle = ho
     result.errorHandle = he
-    #result.outputHandle = open_osfhandle(HO, O_RDONLY)
-    #if result.outputHandle == -1'i32: OSError()
-    #result.errorHandle = open_osfhandle(HE, O_RDONLY)
-    #if result.errorHandle == -1'i32: OSError()
-    var cmdl = buildCommandLine(command, args)
+    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)
+    else:
+      cmdl = buildCommandLine(command, args)
     var wd: cstring = nil
     if len(workingDir) > 0: wd = workingDir
     if env == nil:
@@ -239,6 +252,9 @@ when defined(Windows):
     dealloc(cmdl)
     if success == 0:
       OSError()
+    # NEW:
+    # Close the handles now so anyone waiting is woken.
+    discard closeHandle(procInfo.hThread)
     result.FProcessHandle = procInfo.hProcess
     result.FThreadHandle = procInfo.hThread
     result.id = procInfo.dwProcessID
@@ -258,7 +274,7 @@ when defined(Windows):
       discard TerminateProcess(p.FProcessHandle, 0)
 
   proc waitForExit(p: PProcess): int =
-    discard CloseHandle(p.FThreadHandle)
+    #CloseHandle(p.FThreadHandle)
     discard WaitForSingleObject(p.FProcessHandle, Infinite)
     var res: int32
     discard GetExitCodeProcess(p.FProcessHandle, res)
@@ -274,7 +290,7 @@ when defined(Windows):
   proc errorStream(p: PProcess): PStream =
     result = newFileHandleStream(p.errorHandle)
 
-  proc executeCommand(command: string): int = 
+  proc execCmd(command: string): int = 
     var
       SI: TStartupInfo
       ProcInfo: TProcessInformation
@@ -420,8 +436,8 @@ else:
 
   proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
 
-  proc executeCommand(command: string): int = 
+  proc execCmd(command: string): int = 
     result = csystem(command)
 
 when isMainModule:
-  echo executeCommand("gcc -v")
+  echo execCmd("gcc -v")
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 5704f591c..c26dab099 100755
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -138,7 +138,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
   inc(c.bufpos)               # skip '\'
   case c.buf[c.bufpos]
   of 'n', 'N': 
-    add(tok.literal, nl)
+    add(tok.literal, "\n")
     Inc(c.bufpos)
   of 'r', 'R', 'c', 'C': 
     add(tok.literal, '\c')
@@ -208,7 +208,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
       of '\c', '\L': 
         pos = HandleCRLF(c, pos)
         buf = c.buf
-        add(tok.literal, nl)
+        add(tok.literal, "\n")
       of lexbase.EndOfFile: 
         tok.kind = tkInvalid
         break 
@@ -302,7 +302,7 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
   
 proc errorStr(c: TCfgParser, msg: string): string = 
   result = `%`("$1($2, $3) Error: $4", 
-               [c.filename, toString(getLine(c)), toString(getColumn(c)), msg])
+               [c.filename, $getLine(c), $getColumn(c), msg])
 
 proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = 
   if c.tok.kind == tkSymbol: 
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index 12060ba70..8f4be98f4 100755
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -32,21 +32,10 @@ type
                               ## or the argument, ``value`` is not "" if
                               ## the option was given a value
 
-proc init*(cmdline: string = ""): TOptParser
+proc initOptParser*(cmdline = ""): TOptParser =
   ## inits the option parser. If ``cmdline == ""``, the real command line
   ## (as provided by the ``OS`` module) is taken.
-
-proc next*(p: var TOptParser)
-  ## parses the first or next option; ``p.kind`` describes what token has been
-  ## parsed. ``p.key`` and ``p.val`` are set accordingly.
-
-proc getRestOfCommandLine*(p: TOptParser): string
-  ## retrieves the rest of the command line that has not been parsed yet.
-
-# implementation
-
-proc init(cmdline: string = ""): TOptParser = 
-  result.pos = strStart
+  result.pos = 0
   result.inShortState = false
   if cmdline != "": 
     result.cmd = cmdline
@@ -58,6 +47,10 @@ proc init(cmdline: string = ""): TOptParser =
   result.key = ""
   result.val = ""
 
+proc init*(cmdline: string = ""): TOptParser {.deprecated.} = 
+  ## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
+  result = initOptParser(cmdline)
+
 proc parseWord(s: string, i: int, w: var string, 
                delim: TCharSet = {'\x09', ' ', '\0'}): int = 
   result = i
@@ -89,7 +82,9 @@ proc handleShortOption(p: var TOptParser) =
   if p.cmd[i] == '\0': p.inShortState = false
   p.pos = i
 
-proc next(p: var TOptParser) = 
+proc next*(p: var TOptParser) = 
+  ## parses the first or next option; ``p.kind`` describes what token has been
+  ## parsed. ``p.key`` and ``p.val`` are set accordingly.
   var i = p.pos
   while p.cmd[i] in {'\x09', ' '}: inc(i)
   p.pos = i
@@ -121,12 +116,17 @@ proc next(p: var TOptParser) =
     p.kind = cmdArgument
     p.pos = parseWord(p.cmd, i, p.key)
 
-proc getRestOfCommandLine(p: TOptParser): string = 
-  result = strip(copy(p.cmd, p.pos + strStart, len(p.cmd) - 1)) 
+proc cmdLineRest*(p: TOptParser): string = 
+  ## retrieves the rest of the command line that has not been parsed yet.
+  result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1)) 
+
+proc getRestOfCommandLine*(p: TOptParser): string {.deprecated.} = 
+  ## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
+  result = cmdLineRest(p) 
 
 iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
-  ##this is an convenience iterator for iterating over the command line.
-  ##This uses the TOptParser object. Example:
+  ## This is an convenience iterator for iterating over the command line.
+  ## This uses the TOptParser object. Example:
   ##
   ## .. code-block:: nimrod
   ##   var
@@ -143,7 +143,7 @@ iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
   ##   if filename == "":
   ##     # no filename has been given, so we show the help:
   ##     writeHelp()
-  var p = init()
+  var p = initOptParser()
   while true:
     next(p)
     if p.kind == cmdEnd: break
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index 0e7453ae7..2109c273a 100755
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -483,7 +483,7 @@ proc errorStr(L: TSqlLexer, msg: string): string =
 # OR              	left	        logical disjunction
 
 type
-  TSqlNodeKind* = enum
+  TSqlNodeKind* = enum ## kind of SQL abstract syntax tree
     nkNone,
     nkIdent,
     nkStringLit,
@@ -536,17 +536,18 @@ type
     nkEnumDef
     
 type
-  EInvalidSql* = object of EBase    
-  PSqlNode* = ref TSqlNode
-  TSqlNode* = object
-    case kind*: TSqlNodeKind
+  EInvalidSql* = object of EBase  ## Invalid SQL encountered
+  PSqlNode* = ref TSqlNode        ## an SQL abstract syntax tree node
+  TSqlNode* = object              ## an SQL abstract syntax tree node
+    case kind*: TSqlNodeKind      ## kind of syntax tree
     of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
                 nkIntegerLit, nkNumericLit:
-      strVal*: string
+      strVal*: string             ## AST leaf: the identifier, numeric literal
+                                  ## string literal, etc.
     else:
-      sons*: seq[PSqlNode]
+      sons*: seq[PSqlNode]        ## the node's children
 
-  TSqlParser = object of TSqlLexer
+  TSqlParser* = object of TSqlLexer ## SQL parser object
     tok: TToken
 
 proc newNode(k: TSqlNodeKind): PSqlNode =
@@ -1070,7 +1071,17 @@ proc parseStmt(p: var TSqlParser): PSqlNode =
   else:
     sqlError(p, "CREATE expected")
 
-proc parse*(p: var TSqlParser): PSqlNode =
+proc open(p: var TSqlParser, input: PStream, filename: string) =
+  ## opens the parser `p` and assigns the input stream `input` to it.
+  ## `filename` is only used for error messages.
+  open(TSqlLexer(p), input, filename)
+  p.tok.kind = tkInvalid
+  p.tok.literal = ""
+  getTok(p)
+  
+proc parse(p: var TSqlParser): PSqlNode =
+  ## parses the content of `p`'s input stream and returns the SQL AST.
+  ## Syntax errors raise an `EInvalidSql` exception.
   result = newNode(nkStmtList)
   while p.tok.kind != tkEof:
     var s = parseStmt(p)
@@ -1078,21 +1089,21 @@ proc parse*(p: var TSqlParser): PSqlNode =
     result.add(s)
   if result.len == 1:
     result = result.sons[0]
-
-proc open*(p: var TSqlParser, input: PStream, filename: string) =
-  open(TSqlLexer(p), input, filename)
-  p.tok.kind = tkInvalid
-  p.tok.literal = ""
-  getTok(p)
   
-proc close*(p: var TSqlParser) = 
+proc close(p: var TSqlParser) =
+  ## closes the parser `p`. The associated input stream is closed too.
   close(TSqlLexer(p))
 
 proc parseSQL*(input: PStream, filename: string): PSqlNode =
+  ## parses the SQL from `input` into an AST and returns the AST. 
+  ## `filename` is only used for error messages.
+  ## Syntax errors raise an `EInvalidSql` exception.
   var p: TSqlParser
   open(p, input, filename)
-  result = parse(p)
-  close(p)
+  try:
+    result = parse(p)
+  finally:
+    close(p)
 
 proc ra(n: PSqlNode, s: var string, indent: int)
 
@@ -1114,7 +1125,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) =
     if allCharsInSet(n.strVal, {'\33'..'\127'}):
       s.add(n.strVal)
     else:
-      s.add("\"" & replaceStr(n.strVal, "\"", "\"\"") & "\"")
+      s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"")
   of nkStringLit:
     s.add(escape(n.strVal, "e'", "'"))
   of nkBitStringLit:
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 242a4f01c..54f62a9a4 100755
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -297,7 +297,7 @@ proc parseEntity(my: var TXmlParser, dest: var string) =
   var buf = my.buf
   my.kind = xmlCharData
   if buf[pos] == '#':
-    var r: TRune
+    var r: int
     inc(pos)
     if buf[pos] == 'x': 
       inc(pos)
@@ -312,7 +312,7 @@ proc parseEntity(my: var TXmlParser, dest: var string) =
       while buf[pos] in {'0'..'9'}: 
         r = r * 10 + (ord(buf[pos]) - ord('0'))
         inc(pos)
-    add(dest, toUTF8(r))
+    add(dest, toUTF8(TRune(r)))
   elif buf[pos] == 'l' and buf[pos+1] == 't':
     add(dest, '<')
     inc(pos, 2)
diff --git a/lib/pure/regexprs.nim b/lib/pure/regexprs.nim
index 3a6ca72ae..54f9dc471 100755
--- a/lib/pure/regexprs.nim
+++ b/lib/pure/regexprs.nim
@@ -8,6 +8,9 @@
 #
 
 ## 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
@@ -131,13 +134,14 @@ template `=~` *(s, pattern: expr): expr =
   ##     echo("comment: ", matches[1])
   ##   else:
   ##     echo("syntax error")
-  ## 
-  var matches: array[0..maxSubPatterns-1, string]
+  ##
+  when not definedInScope(matches):
+    var matches: array[0..maxSubPatterns-1, string]
   match(s, pattern, matches)
   
 
 const ## common regular expressions
-  reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"  ## describes an identifier
+  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
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 0aab16214..cfbfef810 100755
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -45,9 +45,9 @@ 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 somehow
+#  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
@@ -272,43 +272,39 @@ iterator splitLines*(s: string): string =
     else: break # was '\0'
     first = last
 
-template iterToProc(iter: expr): stmt = 
-  result = @[]
-  for x in iter: add(result, x)
-
 proc splitLinesSeq*(s: string): seq[string] {.noSideEffect, deprecated.} =
   ## The same as `splitLines`, but is a proc that returns a sequence 
   ## of substrings.
   ## **Deprecated since version 0.8.0**: Use `splitLines` instead.
-  iterToProc(splitLines(s))
+  accumulateResult(splitLines(s))
 
 proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
   noSideEffect, deprecated.} =
   ## The same as `split`, but is a proc that returns a sequence of substrings.
   ## **Deprecated since version 0.8.0**: Use `split` instead.
-  iterToProc(split(s, seps))
+  accumulateResult(split(s, seps))
 
 proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect, 
                                                     deprecated.} =
   ## The same as `split`, but is a proc that returns a sequence of substrings.
   ## **Deprecated since version 0.8.0**: Use `split` instead.
-  iterToProc(split(s, sep))
+  accumulateResult(split(s, sep))
 
 proc splitLines*(s: string): seq[string] {.noSideEffect.} =
   ## The same as the `splitLines` iterator, but is a proc that returns a 
   ## sequence of substrings.
-  iterToProc(splitLines(s))
+  accumulateResult(splitLines(s))
 
 proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
   noSideEffect.} =
   ## The same as the `split` iterator, but is a proc that returns a
   ## sequence of substrings.
-  iterToProc(split(s, seps))
+  accumulateResult(split(s, seps))
 
 proc split*(s: string, sep: char): seq[string] {.noSideEffect.} =
   ## The same as the `split` iterator, but is a proc that returns a sequence
   ## of substrings.
-  iterToProc(split(s, sep))
+  accumulateResult(split(s, sep))
 
 proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
   ## Compares two strings in a case insensitive manner. Returns:
@@ -358,8 +354,10 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
   ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
 
 # the stringify and format operators:
-proc toString*[Ty](x: Ty): string
+proc toString*[Ty](x: Ty): string {.deprecated.}
   ## This generic proc is the same as the stringify operator `$`.
+  ##
+  ## **Deprecated since version 0.8.2:** Use `$` instead.
 
 proc repeatChar*(count: int, c: Char = ' '): string
   ## Returns a string of length `count` consisting only of
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 735713cf5..1edddecd9 100755
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -12,9 +12,14 @@
 {.deadCodeElim: on.}
 
 type
-  TRune* = int ## type that can hold any Unicode character
-  TRune16* = int16 ## 16 bit Unicode character
+  irune = int # underlying type of TRune
+  TRune* = distinct irune   ## type that can hold any Unicode character
+  TRune16* = distinct int16 ## 16 bit Unicode character
   
+proc `<=%`*(a, b: TRune): bool {.borrow.}
+proc `<%`*(a, b: TRune): bool {.borrow.}
+proc `==`*(a, b: TRune): bool {.borrow.}
+
 template ones(n: expr): expr = ((1 shl n)-1)
 
 proc runeLen*(s: string): int =
@@ -28,77 +33,71 @@ proc runeLen*(s: string): int =
     else: assert(false)
     inc(result)
 
-proc runeAt*(s: string, i: int): TRune =
-  ## returns the unicode character in `s` at byte index `i`
-  if ord(s[i]) <=% 127:
-    result = ord(s[i])
-  elif ord(s[i]) shr 5 == 0b110:
-    assert(ord(s[i+1]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6))
-  elif ord(s[i]) shr 4 == 0b1110:
-    assert(ord(s[i+1]) shr 6 == 0b10)
-    assert(ord(s[i+2]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(4)) shl 12 or
-             (ord(s[i+1]) and ones(6)) shl 6 or
-             (ord(s[i+2]) and ones(6))
-  elif ord(s[i]) shr 3 == 0b11110:
-    assert(ord(s[i+1]) shr 6 == 0b10)
-    assert(ord(s[i+2]) shr 6 == 0b10)
-    assert(ord(s[i+3]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(3)) shl 18 or
-             (ord(s[i+1]) and ones(6)) shl 12 or
-             (ord(s[i+2]) and ones(6)) shl 6 or
-             (ord(s[i+3]) and ones(6))
-  else:
-    assert(false)
+proc runeLenAt*(s: string, i: int): int =
+  ## returns the number of bytes the rune starting at ``s[i]`` takes.
+  if ord(s[i]) <=% 127: result = 1
+  elif ord(s[i]) shr 5 == 0b110: result = 2
+  elif ord(s[i]) shr 4 == 0b1110: result = 3
+  elif ord(s[i]) shr 3 == 0b11110: result = 4
+  else: assert(false)
+
+template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
+  ## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true``
+  ## `i` is incremented by the number of bytes that have been processed.
+  when not defined(ones):
+    template ones(n: expr): expr = ((1 shl n)-1)
 
-template fastRuneAt(s, i, result: expr): stmt = 
   if ord(s[i]) <=% 127:
-    result = ord(s[i])
-    inc(i)
+    result = TRune(ord(s[i]))
+    when doInc: inc(i)
   elif ord(s[i]) shr 5 == 0b110:
     assert(ord(s[i+1]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6))
-    inc(i, 2)
+    result = TRune((ord(s[i]) and ones(5)) shl 6 or (ord(s[i+1]) and ones(6)))
+    when doInc: inc(i, 2)
   elif ord(s[i]) shr 4 == 0b1110:
     assert(ord(s[i+1]) shr 6 == 0b10)
     assert(ord(s[i+2]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(4)) shl 12 or
+    result = TRune((ord(s[i]) and ones(4)) shl 12 or
              (ord(s[i+1]) and ones(6)) shl 6 or
-             (ord(s[i+2]) and ones(6))
-    inc(i, 3)
+             (ord(s[i+2]) and ones(6)))
+    when doInc: inc(i, 3)
   elif ord(s[i]) shr 3 == 0b11110:
     assert(ord(s[i+1]) shr 6 == 0b10)
     assert(ord(s[i+2]) shr 6 == 0b10)
     assert(ord(s[i+3]) shr 6 == 0b10)
-    result = (ord(s[i]) and ones(3)) shl 18 or
+    result = TRune((ord(s[i]) and ones(3)) shl 18 or
              (ord(s[i+1]) and ones(6)) shl 12 or
              (ord(s[i+2]) and ones(6)) shl 6 or
-             (ord(s[i+3]) and ones(6))
-    inc(i, 4)
+             (ord(s[i+3]) and ones(6)))
+    when doInc: inc(i, 4)
   else:
     assert(false)
 
+proc runeAt*(s: string, i: int): TRune =
+  ## returns the unicode character in `s` at byte index `i`
+  fastRuneAt(s, i, result, false)
+
 proc toUTF8*(c: TRune): string = 
-  ## converts a character into its UTF8 representation
-  if c <=% 127:
+  ## converts a rune into its UTF8 representation
+  var i = irune(c)
+  if i <=% 127:
     result = newString(1)
-    result[0] = chr(c)
-  elif c <=% 0x07FF:
+    result[0] = chr(i)
+  elif i <=% 0x07FF:
     result = newString(2)
-    result[0] = chr(c shr 6 or 0b110_0000)
-    result[1] = chr(c and ones(6) or 0b10_000000)
-  elif c <=% 0xFFFF:
+    result[0] = chr(i shr 6 or 0b110_0000)
+    result[1] = chr(i and ones(6) or 0b10_000000)
+  elif i <=% 0xFFFF:
     result = newString(3)
-    result[0] = chr(c shr 12 or 0b1110_0000)
-    result[1] = chr(c shr 6 and ones(6) or 0b10_0000_00)
-    result[2] = chr(c and ones(6) or 0b10_0000_00)
-  elif c <=% 0x0010FFFF:
+    result[0] = chr(i shr 12 or 0b1110_0000)
+    result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    result[2] = chr(i and ones(6) or 0b10_0000_00)
+  elif i <=% 0x0010FFFF:
     result = newString(4)
-    result[0] = chr(c shr 18 or 0b1111_0000)
-    result[1] = chr(c shr 12 and ones(6) or 0b10_0000_00)
-    result[2] = chr(c shr 6 and ones(6) or 0b10_0000_00)
-    result[3] = chr(c and ones(6) or 0b10_0000_00)
+    result[0] = chr(i shr 18 or 0b1111_0000)
+    result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
+    result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    result[3] = chr(i and ones(6) or 0b10_0000_00)
   else:
     assert false
 
@@ -1061,7 +1060,7 @@ const
     0x01f1, 501,  # DZ Dz   
     0x01f3, 499]  # dz Dz   
 
-proc binarySearch(c: TRune, tab: openArray[TRune], len, stride: int): int = 
+proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int = 
   var n = len
   var t = 0
   while n > 1: 
@@ -1078,32 +1077,39 @@ proc binarySearch(c: TRune, tab: openArray[TRune], len, stride: int): int =
 
 proc toLower*(c: TRune): TRune = 
   ## Converts `c` into lower case. This works for any Unicode character.
+  ## If possible, prefer `toLower` over `toUpper`. 
+  var c = irune(c)
   var p = binarySearch(c, tolowerRanges, len(toLowerRanges) div 3, 3)
   if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
-    return c + tolowerRanges[p+2] - 500
+    return TRune(c + tolowerRanges[p+2] - 500)
   p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
   if p >= 0 and c == toLowerSinglets[p]:
-    return c + toLowerSinglets[p+1] - 500
-  return c
+    return TRune(c + toLowerSinglets[p+1] - 500)
+  return TRune(c)
 
 proc toUpper*(c: TRune): TRune = 
   ## Converts `c` into upper case. This works for any Unicode character.
+  ## If possible, prefer `toLower` over `toUpper`. 
+  var c = irune(c)
   var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
   if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
-    return c + toUpperRanges[p+2] - 500
+    return TRune(c + toUpperRanges[p+2] - 500)
   p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
   if p >= 0 and c == toUpperSinglets[p]:
-    return c + toUpperSinglets[p+1] - 500
-  return c
+    return TRune(c + toUpperSinglets[p+1] - 500)
+  return TRune(c)
 
 proc toTitle*(c: TRune): TRune = 
+  var c = irune(c)
   var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
   if p >= 0 and c == toTitleSinglets[p]:
-    return c + toTitleSinglets[p+1] - 500
-  return c
+    return TRune(c + toTitleSinglets[p+1] - 500)
+  return TRune(c)
 
 proc isLower*(c: TRune): bool = 
   ## returns true iff `c` is a lower case Unicode character
+  ## If possible, prefer `isLower` over `isUpper`. 
+  var c = irune(c)
   # Note: toUpperRanges is correct here!
   var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
   if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
@@ -1114,6 +1120,8 @@ proc isLower*(c: TRune): bool =
 
 proc isUpper*(c: TRune): bool = 
   ## returns true iff `c` is a upper case Unicode character
+  ## If possible, prefer `isLower` over `isUpper`. 
+  var c = irune(c)
   # Note: toLowerRanges is correct here!
   var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3)
   if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]:
@@ -1126,6 +1134,7 @@ proc isAlpha*(c: TRune): bool =
   ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
   if isUpper(c) or isLower(c): 
     return true
+  var c = irune(c)
   var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2)
   if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]:
     return true
@@ -1138,6 +1147,7 @@ proc isTitle*(c: TRune): bool =
 
 proc isWhiteSpace*(c: TRune): bool = 
   ## returns true iff `c` is a Unicode whitespace character
+  var c = irune(c)
   var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
   if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
     return true
@@ -1148,7 +1158,7 @@ iterator runes*(s: string): TRune =
     i = 0
     result: TRune
   while i < len(s):
-    fastRuneAt(s, i, result)
+    fastRuneAt(s, i, result, true)
     yield result
 
 proc cmpRunesIgnoreCase*(a, b: string): int = 
@@ -1164,7 +1174,7 @@ proc cmpRunesIgnoreCase*(a, b: string): int =
     # slow path:
     fastRuneAt(a, i, ar)
     fastRuneAt(b, j, br)
-    result = toLower(ar) - toLower(br)
+    result = irune(toLower(ar)) - irune(toLower(br))
     if result != 0: return
   result = a.len - b.len
 
diff --git a/lib/system.nim b/lib/system.nim
index 51e5752f2..9247c47c2 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -13,9 +13,6 @@
 ## Each module implicitly imports the System module; it may not be listed
 ## explicitly. Because of this there can not be a user-defined module named
 ## ``system``.
-##
-##   *The good thing about reinventing the wheel is that you can get a
-##   round one.*
 
 {.push hints: off.}
 
@@ -58,11 +55,10 @@ proc defined*[T](x: T): bool {.magic: "Defined", noSideEffect.}
   ##     # provide our own toUpper proc here, because strutils is
   ##     # missing it.
 
-proc definedInScope*[T](x: T, scope=0): bool {.
+proc definedInScope*[T](x: T): bool {.
   magic: "DefinedInScope", noSideEffect.}
   ## Special comile-time procedure that checks whether `x` is
-  ## defined in the scope `scope`. `x` has to be an identifier.
-  ## 0 means the current scope, 1 means the scope above the current scope, etc.
+  ## defined in the current scope. `x` has to be an identifier.
 
 # these require compiler magic:
 proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
@@ -617,6 +613,27 @@ proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
   ## sequences with the array constructor: ``@[1, 2, 3]`` has the type 
   ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. 
 
+proc setLen*[T](s: var seq[T], newlen: int) {.
+  magic: "SetLengthSeq", noSideEffect.}
+  ## sets the length of `s` to `newlen`.
+  ## ``T`` may be any sequence type.
+  ## If the current length is greater than the new length,
+  ## ``s`` will be truncated.
+
+proc setLen*(s: var string, newlen: int) {.
+  magic: "SetLengthStr", noSideEffect.}
+  ## sets the length of `s` to `newlen`.
+  ## If the current length is greater than the new length,
+  ## ``s`` will be truncated.
+
+proc newString*(len: int): string {.
+  magic: "NewString", importc: "mnewString", noSideEffect.}
+  ## returns a new string of length ``len`` but with uninitialized
+  ## content. One needs to fill the string character after character
+  ## with the index operator ``s[i]``. This procedure exists only for
+  ## optimization purposes; the same effect can be achieved with the
+  ## ``&`` operator.
+
 # concat operator:
 proc `&` * (x: string, y: char): string {.
   magic: "ConStrStr", noSideEffect, merge.}
@@ -651,12 +668,15 @@ else:
     """
 
 proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
-proc add *[T](x: var seq[T], y: seq[T]) {.magic: "AppendSeqSeq", noSideEffect.}
+proc add *[T](x: var seq[T], y: seq[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.
   ## Generic code becomes much easier to write if the Nimrod naming scheme is
   ## respected.
+  var xl = x.len
+  setLen(x, xl + y.len)
+  for i in 0..high(y): x[xl+i] = y[i]
 
 proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
   ## takes any Nimrod variable and returns its string representation. It
@@ -798,20 +818,6 @@ proc copy*(s: string, first, last: int): string {.
   ## the first and last characters that shall be copied. If ``last``
   ## is omitted, it is treated as ``high(s)``.
 
-proc setLen*(s: var string, newlen: int) {.
-  magic: "SetLengthStr", noSideEffect.}
-  ## sets the length of `s` to `newlen`.
-  ## If the current length is greater than the new length,
-  ## ``s`` will be truncated.
-
-proc newString*(len: int): string {.
-  magic: "NewString", importc: "mnewString", noSideEffect.}
-  ## returns a new string of length ``len`` but with uninitialized
-  ## content. One needs to fill the string character after character
-  ## with the index operator ``s[i]``. This procedure exists only for
-  ## optimization purposes; the same effect can be achieved with the
-  ## ``&`` operator.
-
 proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
   ## overwrites the contents of the memory at ``p`` with the value 0.
   ## Exactly ``size`` bytes will be overwritten. Like any procedure
@@ -861,13 +867,6 @@ proc dealloc*(p: Pointer) {.noconv.}
   ## or other memory may be corrupted. So this procedure is really
   ## *unsafe*.
 
-proc setLen*[T](s: var seq[T], newlen: int) {.
-  magic: "SetLengthSeq", noSideEffect.}
-  ## sets the length of `s` to `newlen`.
-  ## ``T`` may be any sequence type.
-  ## If the current length is greater than the new length,
-  ## ``s`` will be truncated.
-
 proc assert*(cond: bool) {.magic: "Assert", noSideEffect.}
   ## provides a means to implement `programming by contracts` in Nimrod.
   ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
@@ -1195,6 +1194,10 @@ proc GC_unref*[T](x: seq[T]) {.magic: "GCunref".}
 proc GC_unref*(x: string) {.magic: "GCunref".}
   ## see the documentation of `GC_ref`.
 
+template accumulateResult*(iter: expr) =
+  ## helps to convert an iterator to a proc.
+  result = @[]
+  for x in iter: add(result, x)
 
 {.push checks: off, line_dir: off, debugger: off.}  
 # obviously we cannot generate checking operations here :-)
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 783bce6af..808941c06 100755
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -74,26 +74,16 @@ proc hashString(s: string): int {.compilerproc.} =
   h = h +% h shl 15
   result = h
 
-#  copy(s: string, start = 0): string
-#    {.extern: "copyStr", noDecl, noSideEffect.}
-#  copy(s: string, start, len: int): string
-#    {.extern: "copyStrLen", noDecl, noSideEffect.}
-#
-#  setLength(var s: string, newlen: int)
-#    {.extern: "setLengthStr", noDecl, noSideEffect.}
-
 proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} =
-  var
-    len: int
-  if start >= s.len: return mnewString(0) # BUGFIX
-  if last >= s.len:
-    len = s.len - start # - 1 + 1
+  var start = max(start, 0)
+  var len = min(last, s.len-1) - start + 1
+  if len > 0:
+    result = rawNewString(len)
+    result.len = len
+    c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char))
+    result.data[len] = '\0'
   else:
-    len = last - start + 1
-  result = rawNewString(len)
-  result.len = len
-  c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char))
-  result.data[len] = '\0'
+    result = mnewString(0)
 
 proc copyStr(s: NimString, start: int): NimString {.exportc.} =
   return copyStrLast(s, start, s.len-1)
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index b497ddad3..c0fbe2bb8 100755
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -1,110 +1,110 @@
-#

-#

-#            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 a small wrapper for some needed Win API procedures,

-## so that the Nimrod compiler does not depend on the huge Windows module.

-

-type

-  THandle* = int

-  WINBOOL* = int32

-

-  TSECURITY_ATTRIBUTES* {.final, pure.} = object

-    nLength*: int32

-    lpSecurityDescriptor*: pointer

-    bInheritHandle*: WINBOOL

-  

-  TSTARTUPINFO* {.final, pure.} = object

-    cb*: int32

-    lpReserved*: cstring

-    lpDesktop*: cstring

-    lpTitle*: cstring

-    dwX*: int32

-    dwY*: int32

-    dwXSize*: int32

-    dwYSize*: int32

-    dwXCountChars*: int32

-    dwYCountChars*: int32

-    dwFillAttribute*: int32

-    dwFlags*: int32

-    wShowWindow*: int16

-    cbReserved2*: int16

-    lpReserved2*: pointer

-    hStdInput*: THANDLE

-    hStdOutput*: THANDLE

-    hStdError*: THANDLE

-

-  TPROCESS_INFORMATION* {.final, pure.} = object

-    hProcess*: THANDLE

-    hThread*: THANDLE

-    dwProcessId*: int32

-    dwThreadId*: int32

-

-const

-  STARTF_USESHOWWINDOW* = 1'i32

-  STARTF_USESTDHANDLES* = 256'i32

-  HIGH_PRIORITY_CLASS* = 128'i32

-  IDLE_PRIORITY_CLASS* = 64'i32

-  NORMAL_PRIORITY_CLASS* = 32'i32

-  REALTIME_PRIORITY_CLASS* = 256'i32

-  WAIT_TIMEOUT* = 0x00000102'i32

-  INFINITE* = -1'i32

-

-  STD_INPUT_HANDLE* = -10'i32

-  STD_OUTPUT_HANDLE* = -11'i32

-  STD_ERROR_HANDLE* = -12'i32

-

-proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",

-    importc: "CloseHandle".}

-    

-proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,

-               lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.

-    stdcall, dynlib: "kernel32", importc: "ReadFile".}

-    

-proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,

-                lpNumberOfBytesWritten: var int32, 

-                lpOverlapped: pointer): WINBOOL{.

-    stdcall, dynlib: "kernel32", importc: "WriteFile".}

-

-proc CreatePipe*(hReadPipe, hWritePipe: var THandle,

-                 lpPipeAttributes: var TSECURITY_ATTRIBUTES, 

-                 nSize: int32): WINBOOL{.

-    stdcall, dynlib: "kernel32", importc: "CreatePipe".}

-    

-proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,

-                    lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,

-                    lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,

-                    bInheritHandles: WINBOOL, dwCreationFlags: int32,

-                    lpEnvironment: pointer, lpCurrentDirectory: cstring,

-                    lpStartupInfo: var TSTARTUPINFO,

-                    lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.

-    stdcall, dynlib: "kernel32", importc: "CreateProcessA".}

-

-proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",

-    importc: "SuspendThread".}

-proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",

-    importc: "ResumeThread".}

-

-proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {.

-    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}

-

-proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall,

-    dynlib: "kernel32", importc: "TerminateProcess".}

-

-proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {.

-    stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}

-

-proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32",

-    importc: "GetStdHandle".}

-proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall,

-    dynlib: "kernel32", importc: "SetStdHandle".}

-proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",

-    importc: "FlushFileBuffers".}

+#
+#
+#            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 a small wrapper for some needed Win API procedures,
+## so that the Nimrod compiler does not depend on the huge Windows module.
+
+type
+  THandle* = int
+  WINBOOL* = int32
+
+  TSECURITY_ATTRIBUTES* {.final, pure.} = object
+    nLength*: int32
+    lpSecurityDescriptor*: pointer
+    bInheritHandle*: WINBOOL
+  
+  TSTARTUPINFO* {.final, pure.} = object
+    cb*: int32
+    lpReserved*: cstring
+    lpDesktop*: cstring
+    lpTitle*: cstring
+    dwX*: int32
+    dwY*: int32
+    dwXSize*: int32
+    dwYSize*: int32
+    dwXCountChars*: int32
+    dwYCountChars*: int32
+    dwFillAttribute*: int32
+    dwFlags*: int32
+    wShowWindow*: int16
+    cbReserved2*: int16
+    lpReserved2*: pointer
+    hStdInput*: THANDLE
+    hStdOutput*: THANDLE
+    hStdError*: THANDLE
+
+  TPROCESS_INFORMATION* {.final, pure.} = object
+    hProcess*: THANDLE
+    hThread*: THANDLE
+    dwProcessId*: int32
+    dwThreadId*: int32
+
+const
+  STARTF_USESHOWWINDOW* = 1'i32
+  STARTF_USESTDHANDLES* = 256'i32
+  HIGH_PRIORITY_CLASS* = 128'i32
+  IDLE_PRIORITY_CLASS* = 64'i32
+  NORMAL_PRIORITY_CLASS* = 32'i32
+  REALTIME_PRIORITY_CLASS* = 256'i32
+  WAIT_TIMEOUT* = 0x00000102'i32
+  INFINITE* = -1'i32
+
+  STD_INPUT_HANDLE* = -10'i32
+  STD_OUTPUT_HANDLE* = -11'i32
+  STD_ERROR_HANDLE* = -12'i32
+
+proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
+    importc: "CloseHandle".}
+    
+proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,
+               lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.
+    stdcall, dynlib: "kernel32", importc: "ReadFile".}
+    
+proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,
+                lpNumberOfBytesWritten: var int32, 
+                lpOverlapped: pointer): WINBOOL{.
+    stdcall, dynlib: "kernel32", importc: "WriteFile".}
+
+proc CreatePipe*(hReadPipe, hWritePipe: var THandle,
+                 lpPipeAttributes: var TSECURITY_ATTRIBUTES, 
+                 nSize: int32): WINBOOL{.
+    stdcall, dynlib: "kernel32", importc: "CreatePipe".}
+    
+proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,
+                    lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
+                    lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
+                    bInheritHandles: WINBOOL, dwCreationFlags: int32,
+                    lpEnvironment: pointer, lpCurrentDirectory: cstring,
+                    lpStartupInfo: var TSTARTUPINFO,
+                    lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
+    stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
+
+proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
+    importc: "SuspendThread".}
+proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
+    importc: "ResumeThread".}
+
+proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
+
+proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall,
+    dynlib: "kernel32", importc: "TerminateProcess".}
+
+proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
+
+proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32",
+    importc: "GetStdHandle".}
+proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall,
+    dynlib: "kernel32", importc: "SetStdHandle".}
+proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
+    importc: "FlushFileBuffers".}
 
 proc GetLastError*(): int32 {.importc, stdcall, dynlib: "kernel32".}
 proc FormatMessageA*(dwFlags: int32, lpSource: pointer,
diff --git a/lib/wrappers/tre/config.h b/lib/wrappers/tre/config.h
new file mode 100644
index 000000000..1a3a0bc3a
--- /dev/null
+++ b/lib/wrappers/tre/config.h
@@ -0,0 +1,259 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#define ENABLE_NLS 1
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+/* #undef HAVE_ALLOCA */
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+   CoreFoundation framework. */
+#define HAVE_CFLOCALECOPYCURRENT 1
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+   the CoreFoundation framework. */
+#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long' function. */
+#define HAVE_GETOPT_LONG 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define if you have the iconv() function and it works. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the `isascii' function. */
+#define HAVE_ISASCII 1
+
+/* Define to 1 if you have the `isblank' function. */
+#define HAVE_ISBLANK 1
+
+/* Define to 1 if you have the `iswascii' function or macro. */
+/* #undef HAVE_ISWASCII */
+
+/* Define to 1 if you have the `iswblank' function or macro. */
+/* #undef HAVE_ISWBLANK */
+
+/* Define to 1 if you have the `iswctype' function or macro. */
+/* #undef HAVE_ISWCTYPE */
+
+/* Define to 1 if you have the `iswlower' function or macro. */
+/* #undef HAVE_ISWLOWER */
+
+/* Define to 1 if you have the `iswupper' function or macro. */
+/* #undef HAVE_ISWUPPER */
+
+/* Define to 1 if you have the <libutf8.h> header file. */
+/* #undef HAVE_LIBUTF8_H */
+
+/* Define to 1 if you have the `mbrtowc' function or macro. */
+/* #undef HAVE_MBRTOWC */
+
+/* Define to 1 if the system has the type `mbstate_t'. */
+/* #undef HAVE_MBSTATE_T */
+
+/* Define to 1 if you have the `mbtowc' function or macro. */
+/* #undef HAVE_MBTOWC */
+
+/* Define to 1 if you have the <memory.h> header file. */
+/* #undef HAVE_MEMORY_H */
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if the system has the type `reg_errcode_t'. */
+/* #undef HAVE_REG_ERRCODE_T */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+/* #undef HAVE_STDLIB_H */
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+/* #undef HAVE_STRING_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #undef HAVE_SYS_STAT_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #undef HAVE_SYS_TYPES_H */
+
+/* Define to 1 if you have the `towlower' function or macro. */
+/* #undef HAVE_TOWLOWER */
+
+/* Define to 1 if you have the `towupper' function or macro. */
+/* #undef HAVE_TOWUPPER */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have the <wchar.h> header file. */
+/* #undef HAVE_WCHAR_H */
+
+/* Define to 1 if the system has the type `wchar_t'. */
+/* #undef HAVE_WCHAR_T */
+
+/* Define to 1 if you have the `wcschr' function or macro. */
+/* #undef HAVE_WCSCHR */
+
+/* Define to 1 if you have the `wcscpy' function or macro. */
+/* #undef HAVE_WCSCPY */
+
+/* Define to 1 if you have the `wcslen' function or macro. */
+/* #undef HAVE_WCSLEN */
+
+/* Define to 1 if you have the `wcsncpy' function or macro. */
+/* #undef HAVE_WCSNCPY */
+
+/* Define to 1 if you have the `wcsrtombs' function or macro. */
+/* #undef HAVE_WCSRTOMBS */
+
+/* Define to 1 if you have the `wcstombs' function or macro. */
+/* #undef HAVE_WCSTOMBS */
+
+/* Define to 1 if you have the `wctype' function or macro. */
+/* #undef HAVE_WCTYPE */
+
+/* Define to 1 if you have the <wctype.h> header file. */
+/* #undef HAVE_WCTYPE_H */
+
+/* Define to 1 if the system has the type `wint_t'. */
+/* #undef HAVE_WINT_T */
+
+/* Define if you want to disable debug assertions. */
+#define NDEBUG 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "tre"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "tre-general@lists.laurikari.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "TRE"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "TRE 0.7.6"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "tre"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.7.6"
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at runtime.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you want to enable approximate matching functionality. */
+/* #undef TRE_APPROX */
+
+/* Define if you want TRE to print debug messages to stdout. */
+/* #undef TRE_DEBUG */
+
+/* Define to enable multibyte character set support. */
+/* #undef TRE_MULTIBYTE */
+
+/* Define to a field in the regex_t struct where TRE should store a pointer to
+   the internal tre_tnfa_t structure */
+#define TRE_REGEX_T_FIELD value
+
+/* Define to the absolute path to the system regex.h */
+/* #undef TRE_SYSTEM_REGEX_H_PATH */
+
+/* Define if you want TRE to use alloca() instead of malloc() when allocating
+   memory needed for regexec operations. */
+/* #undef TRE_USE_ALLOCA */
+
+/* Define to include the system regex.h from TRE regex.h */
+/* #undef TRE_USE_SYSTEM_REGEX_H */
+
+/* TRE version string. */
+#define TRE_VERSION "0.7.6"
+
+/* TRE version level 1. */
+#define TRE_VERSION_1 0
+
+/* TRE version level 2. */
+#define TRE_VERSION_2 7
+
+/* TRE version level 3. */
+#define TRE_VERSION_3 6
+
+/* Define to enable wide character (wchar_t) support. */
+/* #undef TRE_WCHAR */
+
+/* Version number of package */
+#define VERSION "0.7.6"
+
+/* Define to the maximum value of wchar_t if not already defined elsewhere */
+/* #undef WCHAR_MAX */
+
+/* Define if wchar_t is signed */
+/* #undef WCHAR_T_SIGNED */
+
+/* Define if wchar_t is unsigned */
+/* #undef WCHAR_T_UNSIGNED */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to enable GNU extensions in glibc */
+#define _GNU_SOURCE 1
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define on IRIX */
+/* #undef _REGCOMP_INTERNAL */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
diff --git a/lib/wrappers/tre/tre_all.c b/lib/wrappers/tre/tre_all.c
new file mode 100644
index 000000000..8272657a3
--- /dev/null
+++ b/lib/wrappers/tre/tre_all.c
@@ -0,0 +1,8873 @@
+/*
+  regcomp.c - TRE POSIX compatible regex compilation functions.
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#define ENABLE_NLS 1
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+/* #undef HAVE_ALLOCA */
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+   CoreFoundation framework. */
+#define HAVE_CFLOCALECOPYCURRENT 1
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+   the CoreFoundation framework. */
+#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long' function. */
+#define HAVE_GETOPT_LONG 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define if you have the iconv() function and it works. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the `isascii' function. */
+#define HAVE_ISASCII 1
+
+/* Define to 1 if you have the `isblank' function. */
+#define HAVE_ISBLANK 1
+
+/* Define to 1 if you have the `iswascii' function or macro. */
+/* #undef HAVE_ISWASCII */
+
+/* Define to 1 if you have the `iswblank' function or macro. */
+/* #undef HAVE_ISWBLANK */
+
+/* Define to 1 if you have the `iswctype' function or macro. */
+/* #undef HAVE_ISWCTYPE */
+
+/* Define to 1 if you have the `iswlower' function or macro. */
+/* #undef HAVE_ISWLOWER */
+
+/* Define to 1 if you have the `iswupper' function or macro. */
+/* #undef HAVE_ISWUPPER */
+
+/* Define to 1 if you have the <libutf8.h> header file. */
+/* #undef HAVE_LIBUTF8_H */
+
+/* Define to 1 if you have the `mbrtowc' function or macro. */
+/* #undef HAVE_MBRTOWC */
+
+/* Define to 1 if the system has the type `mbstate_t'. */
+/* #undef HAVE_MBSTATE_T */
+
+/* Define to 1 if you have the `mbtowc' function or macro. */
+/* #undef HAVE_MBTOWC */
+
+/* Define to 1 if you have the <memory.h> header file. */
+/* #undef HAVE_MEMORY_H */
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if the system has the type `reg_errcode_t'. */
+/* #undef HAVE_REG_ERRCODE_T */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+/* #undef HAVE_STDLIB_H */
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+/* #undef HAVE_STRING_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #undef HAVE_SYS_STAT_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #undef HAVE_SYS_TYPES_H */
+
+/* Define to 1 if you have the `towlower' function or macro. */
+/* #undef HAVE_TOWLOWER */
+
+/* Define to 1 if you have the `towupper' function or macro. */
+/* #undef HAVE_TOWUPPER */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have the <wchar.h> header file. */
+/* #undef HAVE_WCHAR_H */
+
+/* Define to 1 if the system has the type `wchar_t'. */
+/* #undef HAVE_WCHAR_T */
+
+/* Define to 1 if you have the `wcschr' function or macro. */
+/* #undef HAVE_WCSCHR */
+
+/* Define to 1 if you have the `wcscpy' function or macro. */
+/* #undef HAVE_WCSCPY */
+
+/* Define to 1 if you have the `wcslen' function or macro. */
+/* #undef HAVE_WCSLEN */
+
+/* Define to 1 if you have the `wcsncpy' function or macro. */
+/* #undef HAVE_WCSNCPY */
+
+/* Define to 1 if you have the `wcsrtombs' function or macro. */
+/* #undef HAVE_WCSRTOMBS */
+
+/* Define to 1 if you have the `wcstombs' function or macro. */
+/* #undef HAVE_WCSTOMBS */
+
+/* Define to 1 if you have the `wctype' function or macro. */
+/* #undef HAVE_WCTYPE */
+
+/* Define to 1 if you have the <wctype.h> header file. */
+/* #undef HAVE_WCTYPE_H */
+
+/* Define to 1 if the system has the type `wint_t'. */
+/* #undef HAVE_WINT_T */
+
+/* Define if you want to disable debug assertions. */
+#define NDEBUG 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "tre"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "tre-general@lists.laurikari.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "TRE"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "TRE 0.7.6"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "tre"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.7.6"
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at runtime.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you want to enable approximate matching functionality. */
+/* #undef TRE_APPROX */
+
+/* Define if you want TRE to print debug messages to stdout. */
+/* #undef TRE_DEBUG */
+
+/* Define to enable multibyte character set support. */
+/* #undef TRE_MULTIBYTE */
+
+/* Define to a field in the regex_t struct where TRE should store a pointer to
+   the internal tre_tnfa_t structure */
+#define TRE_REGEX_T_FIELD value
+
+/* Define to the absolute path to the system regex.h */
+/* #undef TRE_SYSTEM_REGEX_H_PATH */
+
+/* Define if you want TRE to use alloca() instead of malloc() when allocating
+   memory needed for regexec operations. */
+/* #undef TRE_USE_ALLOCA */
+
+/* Define to include the system regex.h from TRE regex.h */
+/* #undef TRE_USE_SYSTEM_REGEX_H */
+
+/* TRE version string. */
+#define TRE_VERSION "0.7.6"
+
+/* TRE version level 1. */
+#define TRE_VERSION_1 0
+
+/* TRE version level 2. */
+#define TRE_VERSION_2 7
+
+/* TRE version level 3. */
+#define TRE_VERSION_3 6
+
+/* Define to enable wide character (wchar_t) support. */
+/* #undef TRE_WCHAR */
+
+/* Version number of package */
+#define VERSION "0.7.6"
+
+/* Define to the maximum value of wchar_t if not already defined elsewhere */
+/* #undef WCHAR_MAX */
+
+/* Define if wchar_t is signed */
+/* #undef WCHAR_T_SIGNED */
+
+/* Define if wchar_t is unsigned */
+/* #undef WCHAR_T_UNSIGNED */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to enable GNU extensions in glibc */
+#define _GNU_SOURCE 1
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define on IRIX */
+/* #undef _REGCOMP_INTERNAL */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/*
+  regex.h - POSIX.2 compatible regexp interface and TRE extensions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifndef TRE_REGEX_H
+#define TRE_REGEX_H 1
+
+/* lib/tre-config.h.  Generated from tre-config.h.in by configure.  */
+/* tre-config.h.in.  This file has all definitions that are needed in
+   `regex.h'.  Note that this file must contain only the bare minimum
+   of definitions without the TRE_ prefix to avoid conflicts between
+   definitions here and definitions included from somewhere else. */
+
+/* Define to 1 if you have the <libutf8.h> header file. */
+/* #undef HAVE_LIBUTF8_H */
+
+/* Define to 1 if the system has the type `reg_errcode_t'. */
+/* #undef HAVE_REG_ERRCODE_T */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #undef HAVE_SYS_TYPES_H */
+
+/* Define to 1 if you have the <wchar.h> header file. */
+/* #undef HAVE_WCHAR_H */
+
+/* Define if you want to enable approximate matching functionality. */
+/* #undef TRE_APPROX */
+
+/* Define to enable multibyte character set support. */
+/* #undef TRE_MULTIBYTE */
+
+/* Define to the absolute path to the system regex.h */
+/* #undef TRE_SYSTEM_REGEX_H_PATH */
+
+/* Define to include the system regex.h from TRE regex.h */
+/* #undef TRE_USE_SYSTEM_REGEX_H */
+
+/* Define to enable wide character (wchar_t) support. */
+/* #undef TRE_WCHAR */
+
+/* TRE version string. */
+#define TRE_VERSION "0.7.6"
+
+/* TRE version level 1. */
+#define TRE_VERSION_1 0
+
+/* TRE version level 2. */
+#define TRE_VERSION_2 7
+
+/* TRE version level 3. */
+#define TRE_VERSION_3 6
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+#ifdef HAVE_LIBUTF8_H
+#include <libutf8.h>
+#endif /* HAVE_LIBUTF8_H */
+
+#ifdef TRE_USE_SYSTEM_REGEX_H
+/* Include the system regex.h to make TRE ABI compatible with the
+   system regex. */
+#include TRE_SYSTEM_REGEX_H_PATH
+#endif /* TRE_USE_SYSTEM_REGEX_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef TRE_USE_SYSTEM_REGEX_H
+
+#ifndef REG_OK
+#define REG_OK 0
+#endif /* !REG_OK */
+
+#ifndef HAVE_REG_ERRCODE_T
+typedef int reg_errcode_t;
+#endif /* !HAVE_REG_ERRCODE_T */
+
+#if !defined(REG_NOSPEC) && !defined(REG_LITERAL)
+#define REG_LITERAL 0x1000
+#endif
+
+/* Extra regcomp() flags. */
+#ifndef REG_BASIC
+#define REG_BASIC	0
+#endif /* !REG_BASIC */
+#define REG_RIGHT_ASSOC (REG_LITERAL << 1)
+#define REG_UNGREEDY    (REG_RIGHT_ASSOC << 1)
+
+/* Extra regexec() flags. */
+#define REG_APPROX_MATCHER	 0x1000
+#define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1)
+
+#else /* !TRE_USE_SYSTEM_REGEX_H */
+
+/* If the we're not using system regex.h, we need to define the
+   structs and enums ourselves. */
+
+typedef int regoff_t;
+typedef struct {
+  size_t re_nsub;  /* Number of parenthesized subexpressions. */
+  void *value;	   /* For internal use only. */
+} regex_t;
+
+typedef struct {
+  regoff_t rm_so;
+  regoff_t rm_eo;
+} regmatch_t;
+
+
+typedef enum {
+  REG_OK = 0,		/* No error. */
+  /* POSIX regcomp() return error codes.  (In the order listed in the
+     standard.)	 */
+  REG_NOMATCH,		/* No match. */
+  REG_BADPAT,		/* Invalid regexp. */
+  REG_ECOLLATE,		/* Unknown collating element. */
+  REG_ECTYPE,		/* Unknown character class name. */
+  REG_EESCAPE,		/* Trailing backslash. */
+  REG_ESUBREG,		/* Invalid back reference. */
+  REG_EBRACK,		/* "[]" imbalance */
+  REG_EPAREN,		/* "\(\)" or "()" imbalance */
+  REG_EBRACE,		/* "\{\}" or "{}" imbalance */
+  REG_BADBR,		/* Invalid content of {} */
+  REG_ERANGE,		/* Invalid use of range operator */
+  REG_ESPACE,		/* Out of memory.  */
+  REG_BADRPT            /* Invalid use of repetition operators. */
+} reg_errcode_t;
+
+/* POSIX regcomp() flags. */
+#define REG_EXTENDED	1
+#define REG_ICASE	(REG_EXTENDED << 1)
+#define REG_NEWLINE	(REG_ICASE << 1)
+#define REG_NOSUB	(REG_NEWLINE << 1)
+
+/* Extra regcomp() flags. */
+#define REG_BASIC	0
+#define REG_LITERAL	(REG_NOSUB << 1)
+#define REG_RIGHT_ASSOC (REG_LITERAL << 1)
+#define REG_UNGREEDY    (REG_RIGHT_ASSOC << 1)
+
+/* POSIX regexec() flags. */
+#define REG_NOTBOL 1
+#define REG_NOTEOL (REG_NOTBOL << 1)
+
+/* Extra regexec() flags. */
+#define REG_APPROX_MATCHER	 (REG_NOTEOL << 1)
+#define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1)
+
+#endif /* !TRE_USE_SYSTEM_REGEX_H */
+
+/* REG_NOSPEC and REG_LITERAL mean the same thing. */
+#if defined(REG_LITERAL) && !defined(REG_NOSPEC)
+#define REG_NOSPEC	REG_LITERAL
+#elif defined(REG_NOSPEC) && !defined(REG_LITERAL)
+#define REG_LITERAL	REG_NOSPEC
+#endif /* defined(REG_NOSPEC) */
+
+/* The maximum number of iterations in a bound expression. */
+#undef RE_DUP_MAX
+#define RE_DUP_MAX 255
+
+/* The POSIX.2 regexp functions */
+extern int
+regcomp(regex_t *preg, const char *regex, int cflags);
+
+extern int
+regexec(const regex_t *preg, const char *string, size_t nmatch,
+	regmatch_t pmatch[], int eflags);
+
+extern size_t
+regerror(int errcode, const regex_t *preg, char *errbuf,
+	 size_t errbuf_size);
+
+extern void
+regfree(regex_t *preg);
+
+#ifdef TRE_WCHAR
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+
+/* Wide character versions (not in POSIX.2). */
+extern int
+regwcomp(regex_t *preg, const wchar_t *regex, int cflags);
+
+extern int
+regwexec(const regex_t *preg, const wchar_t *string,
+	 size_t nmatch, regmatch_t pmatch[], int eflags);
+#endif /* TRE_WCHAR */
+
+/* Versions with a maximum length argument and therefore the capability to
+   handle null characters in the middle of the strings (not in POSIX.2). */
+extern int
+regncomp(regex_t *preg, const char *regex, size_t len, int cflags);
+
+extern int
+regnexec(const regex_t *preg, const char *string, size_t len,
+	 size_t nmatch, regmatch_t pmatch[], int eflags);
+
+#ifdef TRE_WCHAR
+extern int
+regwncomp(regex_t *preg, const wchar_t *regex, size_t len, int cflags);
+
+extern int
+regwnexec(const regex_t *preg, const wchar_t *string, size_t len,
+	  size_t nmatch, regmatch_t pmatch[], int eflags);
+#endif /* TRE_WCHAR */
+
+
+/* Approximate matching parameter struct. */
+typedef struct {
+  int cost_ins;	       /* Default cost of an inserted character. */
+  int cost_del;	       /* Default cost of a deleted character. */
+  int cost_subst;      /* Default cost of a substituted character. */
+  int max_cost;	       /* Maximum allowed cost of a match. */
+
+  int max_ins;	       /* Maximum allowed number of inserts. */
+  int max_del;	       /* Maximum allowed number of deletes. */
+  int max_subst;       /* Maximum allowed number of substitutes. */
+  int max_err;	       /* Maximum allowed number of errors total. */
+} regaparams_t;
+
+/* Approximate matching result struct. */
+typedef struct {
+  size_t nmatch;       /* Length of pmatch[] array. */
+  regmatch_t *pmatch;  /* Submatch data. */
+  int cost;	       /* Cost of the match. */
+  int num_ins;	       /* Number of inserts in the match. */
+  int num_del;	       /* Number of deletes in the match. */
+  int num_subst;       /* Number of substitutes in the match. */
+} regamatch_t;
+
+#ifdef TRE_APPROX
+
+/* Approximate matching functions. */
+extern int
+regaexec(const regex_t *preg, const char *string,
+	 regamatch_t *match, regaparams_t params, int eflags);
+
+extern int
+reganexec(const regex_t *preg, const char *string, size_t len,
+	  regamatch_t *match, regaparams_t params, int eflags);
+#ifdef TRE_WCHAR
+/* Wide character approximate matching. */
+extern int
+regawexec(const regex_t *preg, const wchar_t *string,
+	  regamatch_t *match, regaparams_t params, int eflags);
+
+extern int
+regawnexec(const regex_t *preg, const wchar_t *string, size_t len,
+	   regamatch_t *match, regaparams_t params, int eflags);
+#endif /* TRE_WCHAR */
+
+/* Sets the parameters to default values. */
+extern void
+regaparams_default(regaparams_t *params);
+#endif /* TRE_APPROX */
+
+#ifdef TRE_WCHAR
+typedef wchar_t tre_char_t;
+#else /* !TRE_WCHAR */
+typedef unsigned char tre_char_t;
+#endif /* !TRE_WCHAR */
+
+typedef struct {
+  int (*get_next_char)(tre_char_t *c, unsigned int *pos_add, void *context);
+  void (*rewind)(size_t pos, void *context);
+  int (*compare)(size_t pos1, size_t pos2, size_t len, void *context);
+  void *context;
+} tre_str_source;
+
+extern int
+reguexec(const regex_t *preg, const tre_str_source *string,
+	 size_t nmatch, regmatch_t pmatch[], int eflags);
+
+/* Returns the version string.	The returned string is static. */
+extern char *
+tre_version(void);
+
+/* Returns the value for a config parameter.  The type to which `result'
+   must point to depends of the value of `query', see documentation for
+   more details. */
+extern int
+tre_config(int query, void *result);
+
+enum {
+  TRE_CONFIG_APPROX,
+  TRE_CONFIG_WCHAR,
+  TRE_CONFIG_MULTIBYTE,
+  TRE_CONFIG_SYSTEM_ABI,
+  TRE_CONFIG_VERSION
+};
+
+/* Returns 1 if the compiled pattern has back references, 0 if not. */
+extern int
+tre_have_backrefs(const regex_t *preg);
+
+/* Returns 1 if the compiled pattern uses approximate matching features,
+   0 if not. */
+extern int
+tre_have_approx(const regex_t *preg);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* TRE_REGEX_H */
+
+/* EOF */
+/*
+  tre-internal.h - TRE internal definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifndef TRE_INTERNAL_H
+#define TRE_INTERNAL_H 1
+
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* !HAVE_WCTYPE_H */
+
+#include <ctype.h>
+
+#ifdef TRE_DEBUG
+#include <stdio.h>
+#define DPRINT(msg) do {printf msg; fflush(stdout);} while(/*CONSTCOND*/0)
+#else /* !TRE_DEBUG */
+#define DPRINT(msg) do { } while(/*CONSTCOND*/0)
+#endif /* !TRE_DEBUG */
+
+#define elementsof(x)	( sizeof(x) / sizeof(x[0]) )
+
+#ifdef HAVE_MBRTOWC
+#define tre_mbrtowc(pwc, s, n, ps) (mbrtowc((pwc), (s), (n), (ps)))
+#else /* !HAVE_MBRTOWC */
+#ifdef HAVE_MBTOWC
+#define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n)))
+#endif /* HAVE_MBTOWC */
+#endif /* !HAVE_MBRTOWC */
+
+#ifdef TRE_MULTIBYTE
+#ifdef HAVE_MBSTATE_T
+#define TRE_MBSTATE
+#endif /* TRE_MULTIBYTE */
+#endif /* HAVE_MBSTATE_T */
+
+/* Define the character types and functions. */
+#ifdef TRE_WCHAR
+
+/* Wide characters. */
+typedef wint_t tre_cint_t;
+#define TRE_CHAR_MAX WCHAR_MAX
+
+#ifdef TRE_MULTIBYTE
+#define TRE_MB_CUR_MAX MB_CUR_MAX
+#else /* !TRE_MULTIBYTE */
+#define TRE_MB_CUR_MAX 1
+#endif /* !TRE_MULTIBYTE */
+
+#define tre_isalnum iswalnum
+#define tre_isalpha iswalpha
+#ifdef HAVE_ISWBLANK
+#define tre_isblank iswblank
+#endif /* HAVE_ISWBLANK */
+#define tre_iscntrl iswcntrl
+#define tre_isdigit iswdigit
+#define tre_isgraph iswgraph
+#define tre_islower iswlower
+#define tre_isprint iswprint
+#define tre_ispunct iswpunct
+#define tre_isspace iswspace
+#define tre_isupper iswupper
+#define tre_isxdigit iswxdigit
+
+#define tre_tolower towlower
+#define tre_toupper towupper
+#define tre_strlen  wcslen
+
+#else /* !TRE_WCHAR */
+
+/* 8 bit characters. */
+typedef short tre_cint_t;
+#define TRE_CHAR_MAX 255
+#define TRE_MB_CUR_MAX 1
+
+#define tre_isalnum isalnum
+#define tre_isalpha isalpha
+#ifdef HAVE_ISASCII
+#define tre_isascii isascii
+#endif /* HAVE_ISASCII */
+#ifdef HAVE_ISBLANK
+#define tre_isblank isblank
+#endif /* HAVE_ISBLANK */
+#define tre_iscntrl iscntrl
+#define tre_isdigit isdigit
+#define tre_isgraph isgraph
+#define tre_islower islower
+#define tre_isprint isprint
+#define tre_ispunct ispunct
+#define tre_isspace isspace
+#define tre_isupper isupper
+#define tre_isxdigit isxdigit
+
+#define tre_tolower(c) (tre_cint_t)(tolower(c))
+#define tre_toupper(c) (tre_cint_t)(toupper(c))
+#define tre_strlen(s)  (strlen((const char*)s))
+
+#endif /* !TRE_WCHAR */
+
+#if defined(TRE_WCHAR) && defined(HAVE_ISWCTYPE) && defined(HAVE_WCTYPE)
+#define TRE_USE_SYSTEM_WCTYPE 1
+#endif
+
+#ifdef TRE_USE_SYSTEM_WCTYPE
+/* Use system provided iswctype() and wctype(). */
+typedef wctype_t tre_ctype_t;
+#define tre_isctype iswctype
+#define tre_ctype   wctype
+#else /* !TRE_USE_SYSTEM_WCTYPE */
+/* Define our own versions of iswctype() and wctype(). */
+typedef int (*tre_ctype_t)(tre_cint_t);
+#define tre_isctype(c, type) ( (type)(c) )
+tre_ctype_t tre_ctype(const char *name);
+#endif /* !TRE_USE_SYSTEM_WCTYPE */
+
+typedef enum { STR_WIDE, STR_BYTE, STR_MBS, STR_USER } tre_str_type_t;
+
+/* Returns number of bytes to add to (char *)ptr to make it
+   properly aligned for the type. */
+#define ALIGN(ptr, type) \
+  ((((long)ptr) % sizeof(type)) \
+   ? (sizeof(type) - (((long)ptr) % sizeof(type))) \
+   : 0)
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+
+/* Define STRF to the correct printf formatter for strings. */
+#ifdef TRE_WCHAR
+#define STRF "ls"
+#else /* !TRE_WCHAR */
+#define STRF "s"
+#endif /* !TRE_WCHAR */
+
+/* TNFA transition type. A TNFA state is an array of transitions,
+   the terminator is a transition with NULL `state'. */
+typedef struct tnfa_transition tre_tnfa_transition_t;
+
+struct tnfa_transition {
+  /* Range of accepted characters. */
+  tre_cint_t code_min;
+  tre_cint_t code_max;
+  /* Pointer to the destination state. */
+  tre_tnfa_transition_t *state;
+  /* ID number of the destination state. */
+  int state_id;
+  /* -1 terminated array of tags (or NULL). */
+  int *tags;
+  /* Matching parameters settings (or NULL). */
+  int *params;
+  /* Assertion bitmap. */
+  int assertions;
+  /* Assertion parameters. */
+  union {
+    /* Character class assertion. */
+    tre_ctype_t class;
+    /* Back reference assertion. */
+    int backref;
+  } u;
+  /* Negative character class assertions. */
+  tre_ctype_t *neg_classes;
+};
+
+
+/* Assertions. */
+#define ASSERT_AT_BOL		  1   /* Beginning of line. */
+#define ASSERT_AT_EOL		  2   /* End of line. */
+#define ASSERT_CHAR_CLASS	  4   /* Character class in `class'. */
+#define ASSERT_CHAR_CLASS_NEG	  8   /* Character classes in `neg_classes'. */
+#define ASSERT_AT_BOW		 16   /* Beginning of word. */
+#define ASSERT_AT_EOW		 32   /* End of word. */
+#define ASSERT_AT_WB		 64   /* Word boundary. */
+#define ASSERT_AT_WB_NEG	128   /* Not a word boundary. */
+#define ASSERT_BACKREF		256   /* A back reference in `backref'. */
+#define ASSERT_LAST		256
+
+/* Tag directions. */
+typedef enum {
+  TRE_TAG_MINIMIZE = 0,
+  TRE_TAG_MAXIMIZE = 1
+} tre_tag_direction_t;
+
+/* Parameters that can be changed dynamically while matching. */
+typedef enum {
+  TRE_PARAM_COST_INS	    = 0,
+  TRE_PARAM_COST_DEL	    = 1,
+  TRE_PARAM_COST_SUBST	    = 2,
+  TRE_PARAM_COST_MAX	    = 3,
+  TRE_PARAM_MAX_INS	    = 4,
+  TRE_PARAM_MAX_DEL	    = 5,
+  TRE_PARAM_MAX_SUBST	    = 6,
+  TRE_PARAM_MAX_ERR	    = 7,
+  TRE_PARAM_DEPTH	    = 8,
+  TRE_PARAM_LAST	    = 9
+} tre_param_t;
+
+/* Unset matching parameter */
+#define TRE_PARAM_UNSET -1
+
+/* Signifies the default matching parameter value. */
+#define TRE_PARAM_DEFAULT -2
+
+/* Instructions to compute submatch register values from tag values
+   after a successful match.  */
+struct tre_submatch_data {
+  /* Tag that gives the value for rm_so (submatch start offset). */
+  int so_tag;
+  /* Tag that gives the value for rm_eo (submatch end offset). */
+  int eo_tag;
+  /* List of submatches this submatch is contained in. */
+  int *parents;
+};
+
+typedef struct tre_submatch_data tre_submatch_data_t;
+
+
+/* TNFA definition. */
+typedef struct tnfa tre_tnfa_t;
+
+struct tnfa {
+  tre_tnfa_transition_t *transitions;
+  unsigned int num_transitions;
+  tre_tnfa_transition_t *initial;
+  tre_tnfa_transition_t *final;
+  tre_submatch_data_t *submatch_data;
+  char *firstpos_chars;
+  int first_char;
+  unsigned int num_submatches;
+  tre_tag_direction_t *tag_directions;
+  int *minimal_tags;
+  int num_tags;
+  int num_minimals;
+  int end_tag;
+  int num_states;
+  int cflags;
+  int have_backrefs;
+  int have_approx;
+  int params_depth;
+};
+
+int
+tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags);
+
+void
+tre_free(regex_t *preg);
+
+void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+		const tre_tnfa_t *tnfa, int *tags, int match_eo);
+
+reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
+		      tre_str_type_t type, int *match_tags, int eflags,
+		      int *match_end_ofs);
+
+reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
+		      tre_str_type_t type, int *match_tags, int eflags,
+		      int *match_end_ofs);
+
+reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+		       int len, tre_str_type_t type, int *match_tags,
+		       int eflags, int *match_end_ofs);
+
+#ifdef TRE_APPROX
+reg_errcode_t
+tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, int len,
+		    tre_str_type_t type, int *match_tags,
+		    regamatch_t *match, regaparams_t params,
+		    int eflags, int *match_end_ofs);
+#endif /* TRE_APPROX */
+
+#endif /* TRE_INTERNAL_H */
+
+/* EOF */
+/*
+  xmalloc.h - Simple malloc debugging library API
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifndef _XMALLOC_H
+#define _XMALLOC_H 1
+
+void *xmalloc_impl(size_t size, const char *file, int line, const char *func);
+void *xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
+		   const char *func);
+void xfree_impl(void *ptr, const char *file, int line, const char *func);
+void *xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
+		    const char *func);
+int xmalloc_dump_leaks(void);
+void xmalloc_configure(int fail_after);
+
+
+#ifndef XMALLOC_INTERNAL
+#ifdef MALLOC_DEBUGGING
+
+/* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__'
+   which contains the name of the function currently being defined.
+#  define __XMALLOC_FUNCTION	 __PRETTY_FUNCTION__
+   This is broken in G++ before version 2.6.
+   C9x has a similar variable called __func__, but prefer the GCC one since
+   it demangles C++ function names.  */
+# ifdef __GNUC__
+#  if __GNUC__ > 2 || (__GNUC__ == 2 \
+		       && __GNUC_MINOR__ >= (defined __cplusplus ? 6 : 4))
+#   define __XMALLOC_FUNCTION	 __PRETTY_FUNCTION__
+#  else
+#   define __XMALLOC_FUNCTION	 ((const char *) 0)
+#  endif
+# else
+#  if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#   define __XMALLOC_FUNCTION	 __func__
+#  else
+#   define __XMALLOC_FUNCTION	 ((const char *) 0)
+#  endif
+# endif
+
+#define xmalloc(size) xmalloc_impl(size, __FILE__, __LINE__, \
+				   __XMALLOC_FUNCTION)
+#define xcalloc(nmemb, size) xcalloc_impl(nmemb, size, __FILE__, __LINE__, \
+					  __XMALLOC_FUNCTION)
+#define xfree(ptr) xfree_impl(ptr, __FILE__, __LINE__, __XMALLOC_FUNCTION)
+#define xrealloc(ptr, new_size) xrealloc_impl(ptr, new_size, __FILE__, \
+					      __LINE__, __XMALLOC_FUNCTION)
+#undef malloc
+#undef calloc
+#undef free
+#undef realloc
+
+#define malloc	USE_XMALLOC_INSTEAD_OF_MALLOC
+#define calloc	USE_XCALLOC_INSTEAD_OF_CALLOC
+#define free	USE_XFREE_INSTEAD_OF_FREE
+#define realloc USE_XREALLOC_INSTEAD_OF_REALLOC
+
+#else /* !MALLOC_DEBUGGING */
+
+#include <stdlib.h>
+
+#define xmalloc(size) malloc(size)
+#define xcalloc(nmemb, size) calloc(nmemb, size)
+#define xfree(ptr) free(ptr)
+#define xrealloc(ptr, new_size) realloc(ptr, new_size)
+
+#endif /* !MALLOC_DEBUGGING */
+#endif /* !XMALLOC_INTERNAL */
+
+#endif /* _XMALLOC_H */
+
+/* EOF */
+
+int
+regncomp(regex_t *preg, const char *regex, size_t n, int cflags)
+{
+  int ret;
+#if TRE_WCHAR
+  tre_char_t *wregex;
+  int wlen;
+
+  wregex = xmalloc(sizeof(tre_char_t) * (n + 1));
+  if (wregex == NULL)
+    return REG_ESPACE;
+
+  /* If the current locale uses the standard single byte encoding of
+     characters, we don't do a multibyte string conversion.  If we did,
+     many applications which use the default locale would break since
+     the default "C" locale uses the 7-bit ASCII character set, and
+     all characters with the eighth bit set would be considered invalid. */
+#if TRE_MULTIBYTE
+  if (TRE_MB_CUR_MAX == 1)
+#endif /* TRE_MULTIBYTE */
+    {
+      unsigned int i;
+      const unsigned char *str = (const unsigned char *)regex;
+      tre_char_t *wstr = wregex;
+
+      for (i = 0; i < n; i++)
+	*(wstr++) = *(str++);
+      wlen = n;
+    }
+#if TRE_MULTIBYTE
+  else
+    {
+      int consumed;
+      tre_char_t *wcptr = wregex;
+#ifdef HAVE_MBSTATE_T
+      mbstate_t state;
+      memset(&state, '\0', sizeof(state));
+#endif /* HAVE_MBSTATE_T */
+      while (n > 0)
+	{
+	  consumed = tre_mbrtowc(wcptr, regex, n, &state);
+
+	  switch (consumed)
+	    {
+	    case 0:
+	      if (*regex == '\0')
+		consumed = 1;
+	      else
+		{
+		  xfree(wregex);
+		  return REG_BADPAT;
+		}
+	      break;
+	    case -1:
+	      DPRINT(("mbrtowc: error %d: %s.\n", errno, strerror(errno)));
+	      xfree(wregex);
+	      return REG_BADPAT;
+	    case -2:
+	      /* The last character wasn't complete.  Let's not call it a
+		 fatal error. */
+	      consumed = n;
+	      break;
+	    }
+	  regex += consumed;
+	  n -= consumed;
+	  wcptr++;
+	}
+      wlen = wcptr - wregex;
+    }
+#endif /* TRE_MULTIBYTE */
+
+  wregex[wlen] = L'\0';
+  ret = tre_compile(preg, wregex, (unsigned)wlen, cflags);
+  xfree(wregex);
+#else /* !TRE_WCHAR */
+  ret = tre_compile(preg, (const tre_char_t *)regex, n, cflags);
+#endif /* !TRE_WCHAR */
+
+  return ret;
+}
+
+int
+regcomp(regex_t *preg, const char *regex, int cflags)
+{
+  return regncomp(preg, regex, regex ? strlen(regex) : 0, cflags);
+}
+
+
+#ifdef TRE_WCHAR
+int
+regwncomp(regex_t *preg, const wchar_t *regex, size_t n, int cflags)
+{
+  return tre_compile(preg, regex, n, cflags);
+}
+
+int
+regwcomp(regex_t *preg, const wchar_t *regex, int cflags)
+{
+  return tre_compile(preg, regex, regex ? wcslen(regex) : 0, cflags);
+}
+#endif /* TRE_WCHAR */
+
+void
+regfree(regex_t *preg)
+{
+  tre_free(preg);
+}
+
+/* EOF */
+/*
+  regerror.c - POSIX regerror() implementation for TRE.
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* HAVE_WCTYPE_H */
+
+#define dgettext(p, s) s
+#define gettext(s) s
+
+#define _(String) dgettext(PACKAGE, String)
+#define gettext_noop(String) String
+
+/* Error message strings for error codes listed in `regex.h'.  This list
+   needs to be in sync with the codes listed there, naturally. */
+static const char *tre_error_messages[] =
+  { gettext_noop("No error"),				 /* REG_OK */
+    gettext_noop("No match"),				 /* REG_NOMATCH */
+    gettext_noop("Invalid regexp"),			 /* REG_BADPAT */
+    gettext_noop("Unknown collating element"),		 /* REG_ECOLLATE */
+    gettext_noop("Unknown character class name"),	 /* REG_ECTYPE */
+    gettext_noop("Trailing backslash"),			 /* REG_EESCAPE */
+    gettext_noop("Invalid back reference"),		 /* REG_ESUBREG */
+    gettext_noop("Missing ']'"),			 /* REG_EBRACK */
+    gettext_noop("Missing ')'"),			 /* REG_EPAREN */
+    gettext_noop("Missing '}'"),			 /* REG_EBRACE */
+    gettext_noop("Invalid contents of {}"),		 /* REG_BADBR */
+    gettext_noop("Invalid character range"),		 /* REG_ERANGE */
+    gettext_noop("Out of memory"),			 /* REG_ESPACE */
+    gettext_noop("Invalid use of repetition operators")	 /* REG_BADRPT */
+  };
+
+size_t
+regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+  const char *err;
+  size_t err_len;
+
+  /*LINTED*/(void)&preg;
+  if (errcode >= 0
+      && errcode < (int)(sizeof(tre_error_messages)
+			 / sizeof(*tre_error_messages)))
+    err = gettext(tre_error_messages[errcode]);
+  else
+    err = gettext("Unknown error");
+
+  err_len = strlen(err) + 1;
+  if (errbuf_size > 0 && errbuf != NULL)
+    {
+      if (err_len > errbuf_size)
+	{
+	  strncpy(errbuf, err, errbuf_size - 1);
+	  errbuf[errbuf_size - 1] = '\0';
+	}
+      else
+	{
+	  strcpy(errbuf, err);
+	}
+    }
+  return err_len;
+}
+
+/* EOF */
+/*
+  regexec.c - TRE POSIX compatible matching functions (and more).
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef TRE_USE_ALLOCA
+/* AIX requires this to be the first thing in the file.	 */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+#endif /* TRE_USE_ALLOCA */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* HAVE_WCTYPE_H */
+#ifndef TRE_WCHAR
+#include <ctype.h>
+#endif /* !TRE_WCHAR */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+#include <limits.h>
+
+
+
+/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match
+   endpoint values. */
+void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+		const tre_tnfa_t *tnfa, int *tags, int match_eo)
+{
+  tre_submatch_data_t *submatch_data;
+  unsigned int i, j;
+  int *parents;
+
+  i = 0;
+  if (match_eo >= 0 && !(cflags & REG_NOSUB))
+    {
+      /* Construct submatch offsets from the tags. */
+      DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo));
+      submatch_data = tnfa->submatch_data;
+      while (i < tnfa->num_submatches && i < nmatch)
+	{
+	  if (submatch_data[i].so_tag == tnfa->end_tag)
+	    pmatch[i].rm_so = match_eo;
+	  else
+	    pmatch[i].rm_so = tags[submatch_data[i].so_tag];
+
+	  if (submatch_data[i].eo_tag == tnfa->end_tag)
+	    pmatch[i].rm_eo = match_eo;
+	  else
+	    pmatch[i].rm_eo = tags[submatch_data[i].eo_tag];
+
+	  /* If either of the endpoints were not used, this submatch
+	     was not part of the match. */
+	  if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1)
+	    pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+
+	  DPRINT(("pmatch[%d] = {t%d = %d, t%d = %d}\n", i,
+		  submatch_data[i].so_tag, pmatch[i].rm_so,
+		  submatch_data[i].eo_tag, pmatch[i].rm_eo));
+	  i++;
+	}
+      /* Reset all submatches that are not within all of their parent
+	 submatches. */
+      i = 0;
+      while (i < tnfa->num_submatches && i < nmatch)
+	{
+	  if (pmatch[i].rm_eo == -1)
+	    assert(pmatch[i].rm_so == -1);
+	  assert(pmatch[i].rm_so <= pmatch[i].rm_eo);
+
+	  parents = submatch_data[i].parents;
+	  if (parents != NULL)
+	    for (j = 0; parents[j] >= 0; j++)
+	      {
+		DPRINT(("pmatch[%d] parent %d\n", i, parents[j]));
+		if (pmatch[i].rm_so < pmatch[parents[j]].rm_so
+		    || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo)
+		  pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+	      }
+	  i++;
+	}
+    }
+
+  while (i < nmatch)
+    {
+      pmatch[i].rm_so = -1;
+      pmatch[i].rm_eo = -1;
+      i++;
+    }
+}
+
+
+/*
+  Wrapper functions for POSIX compatible regexp matching.
+*/
+
+int
+tre_have_backrefs(const regex_t *preg)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tnfa->have_backrefs;
+}
+
+int
+tre_have_approx(const regex_t *preg)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tnfa->have_approx;
+}
+
+static int
+tre_match(const tre_tnfa_t *tnfa, const void *string, size_t len,
+	  tre_str_type_t type, size_t nmatch, regmatch_t pmatch[],
+	  int eflags)
+{
+  reg_errcode_t status;
+  int *tags = NULL, eo;
+  if (tnfa->num_tags > 0 && nmatch > 0)
+    {
+#ifdef TRE_USE_ALLOCA
+      tags = alloca(sizeof(*tags) * tnfa->num_tags);
+#else /* !TRE_USE_ALLOCA */
+      tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+#endif /* !TRE_USE_ALLOCA */
+      if (tags == NULL)
+	return REG_ESPACE;
+    }
+
+  /* Dispatch to the appropriate matcher. */
+  if (tnfa->have_backrefs || eflags & REG_BACKTRACKING_MATCHER)
+    {
+      /* The regex has back references, use the backtracking matcher. */
+      if (type == STR_USER)
+	{
+	  const tre_str_source *source = string;
+	  if (source->rewind == NULL || source->compare == NULL)
+	    /* The backtracking matcher requires rewind and compare
+	       capabilities from the input stream. */
+	    return REG_BADPAT;
+	}
+      status = tre_tnfa_run_backtrack(tnfa, string, (int)len, type,
+				      tags, eflags, &eo);
+    }
+#ifdef TRE_APPROX
+  else if (tnfa->have_approx || eflags & REG_APPROX_MATCHER)
+    {
+      /* The regex uses approximate matching, use the approximate matcher. */
+      regamatch_t match;
+      regaparams_t params;
+      regaparams_default(&params);
+      params.max_err = 0;
+      params.max_cost = 0;
+      status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags,
+				   &match, params, eflags, &eo);
+    }
+#endif /* TRE_APPROX */
+  else
+    {
+      /* Exact matching, no back references, use the parallel matcher. */
+      status = tre_tnfa_run_parallel(tnfa, string, (int)len, type,
+				     tags, eflags, &eo);
+    }
+
+  if (status == REG_OK)
+    /* A match was found, so fill the submatch registers. */
+    tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo);
+#ifndef TRE_USE_ALLOCA
+  if (tags)
+    xfree(tags);
+#endif /* !TRE_USE_ALLOCA */
+  return status;
+}
+
+int
+regnexec(const regex_t *preg, const char *str, size_t len,
+	 size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS;
+
+  return tre_match(tnfa, str, len, type, nmatch, pmatch, eflags);
+}
+
+int
+regexec(const regex_t *preg, const char *str,
+	size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  return regnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags);
+}
+
+
+#ifdef TRE_WCHAR
+
+int
+regwnexec(const regex_t *preg, const wchar_t *str, size_t len,
+	  size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tre_match(tnfa, str, len, STR_WIDE, nmatch, pmatch, eflags);
+}
+
+int
+regwexec(const regex_t *preg, const wchar_t *str,
+	 size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  return regwnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags);
+}
+
+#endif /* TRE_WCHAR */
+
+int
+reguexec(const regex_t *preg, const tre_str_source *str,
+	 size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tre_match(tnfa, str, (unsigned)-1, STR_USER, nmatch, pmatch, eflags);
+}
+
+
+#ifdef TRE_APPROX
+
+/*
+  Wrapper functions for approximate regexp matching.
+*/
+
+static int
+tre_match_approx(const tre_tnfa_t *tnfa, const void *string, size_t len,
+		 tre_str_type_t type, regamatch_t *match, regaparams_t params,
+		 int eflags)
+{
+  reg_errcode_t status;
+  int *tags = NULL, eo;
+
+  /* If the regexp does not use approximate matching features, the
+     maximum cost is zero, and the approximate matcher isn't forced,
+     use the exact matcher instead. */
+  if (params.max_cost == 0 && !tnfa->have_approx
+      && !(eflags & REG_APPROX_MATCHER))
+    return tre_match(tnfa, string, len, type, match->nmatch, match->pmatch,
+		     eflags);
+
+  /* Back references are not supported by the approximate matcher. */
+  if (tnfa->have_backrefs)
+    return REG_BADPAT;
+
+  if (tnfa->num_tags > 0 && match->nmatch > 0)
+    {
+#if TRE_USE_ALLOCA
+      tags = alloca(sizeof(*tags) * tnfa->num_tags);
+#else /* !TRE_USE_ALLOCA */
+      tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+#endif /* !TRE_USE_ALLOCA */
+      if (tags == NULL)
+	return REG_ESPACE;
+    }
+  status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags,
+			       match, params, eflags, &eo);
+  if (status == REG_OK)
+    tre_fill_pmatch(match->nmatch, match->pmatch, tnfa->cflags, tnfa, tags, eo);
+#ifndef TRE_USE_ALLOCA
+  if (tags)
+    xfree(tags);
+#endif /* !TRE_USE_ALLOCA */
+  return status;
+}
+
+int
+reganexec(const regex_t *preg, const char *str, size_t len,
+	  regamatch_t *match, regaparams_t params, int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS;
+
+  return tre_match_approx(tnfa, str, len, type, match, params, eflags);
+}
+
+int
+regaexec(const regex_t *preg, const char *str,
+	 regamatch_t *match, regaparams_t params, int eflags)
+{
+  return reganexec(preg, str, (unsigned)-1, match, params, eflags);
+}
+
+#ifdef TRE_WCHAR
+
+int
+regawnexec(const regex_t *preg, const wchar_t *str, size_t len,
+	   regamatch_t *match, regaparams_t params, int eflags)
+{
+  tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  return tre_match_approx(tnfa, str, len, STR_WIDE,
+			  match, params, eflags);
+}
+
+int
+regawexec(const regex_t *preg, const wchar_t *str,
+	  regamatch_t *match, regaparams_t params, int eflags)
+{
+  return regawnexec(preg, str, (unsigned)-1, match, params, eflags);
+}
+
+#endif /* TRE_WCHAR */
+
+void
+regaparams_default(regaparams_t *params)
+{
+  memset(params, 0, sizeof(*params));
+  params->cost_ins = 1;
+  params->cost_del = 1;
+  params->cost_subst = 1;
+  params->max_cost = INT_MAX;
+  params->max_ins = INT_MAX;
+  params->max_del = INT_MAX;
+  params->max_subst = INT_MAX;
+  params->max_err = INT_MAX;
+}
+
+#endif /* TRE_APPROX */
+
+/* EOF */
+/*
+  tre-ast.c - Abstract syntax tree (AST) routines
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <assert.h>
+
+/*
+  tre-ast.h - Abstract syntax tree (AST) definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+
+#ifndef TRE_AST_H
+#define TRE_AST_H 1
+
+/*
+  tre-mem.h - TRE memory allocator interface
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifndef TRE_MEM_H
+#define TRE_MEM_H 1
+
+#include <stdlib.h>
+
+#define TRE_MEM_BLOCK_SIZE 1024
+
+typedef struct tre_list {
+  void *data;
+  struct tre_list *next;
+} tre_list_t;
+
+typedef struct tre_mem_struct {
+  tre_list_t *blocks;
+  tre_list_t *current;
+  char *ptr;
+  size_t n;
+  int failed;
+  void **provided;
+} *tre_mem_t;
+
+
+tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
+void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+			 int zero, size_t size);
+
+/* Returns a new memory allocator or NULL if out of memory. */
+#define tre_mem_new()  tre_mem_new_impl(0, NULL)
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed. */
+#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size)
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed.  The memory
+   is set to zero. */
+#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size)
+
+#ifdef TRE_USE_ALLOCA
+/* alloca() versions.  Like above, but memory is allocated with alloca()
+   instead of malloc(). */
+
+#define tre_mem_newa() \
+  tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct)))
+
+#define tre_mem_alloca(mem, size)					      \
+  ((mem)->n >= (size)							      \
+   ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size))			      \
+   : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size)))
+#endif /* TRE_USE_ALLOCA */
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+void tre_mem_destroy(tre_mem_t mem);
+
+#endif /* TRE_MEM_H */
+
+/* EOF */
+/*
+  tre-compile.h: Regex compilation definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+
+#ifndef TRE_COMPILE_H
+#define TRE_COMPILE_H 1
+
+typedef struct {
+  int position;
+  int code_min;
+  int code_max;
+  int *tags;
+  int assertions;
+  tre_ctype_t class;
+  tre_ctype_t *neg_classes;
+  int backref;
+  int *params;
+} tre_pos_and_tags_t;
+
+#endif /* TRE_COMPILE_H */
+
+/* EOF */
+
+/* The different AST node types. */
+typedef enum {
+  LITERAL,
+  CATENATION,
+  ITERATION,
+  UNION
+} tre_ast_type_t;
+
+/* Special subtypes of TRE_LITERAL. */
+#define EMPTY	  -1   /* Empty leaf (denotes empty string). */
+#define ASSERTION -2   /* Assertion leaf. */
+#define TAG	  -3   /* Tag leaf. */
+#define BACKREF	  -4   /* Back reference leaf. */
+#define PARAMETER -5   /* Parameter. */
+
+#define IS_SPECIAL(x)	((x)->code_min < 0)
+#define IS_EMPTY(x)	((x)->code_min == EMPTY)
+#define IS_ASSERTION(x) ((x)->code_min == ASSERTION)
+#define IS_TAG(x)	((x)->code_min == TAG)
+#define IS_BACKREF(x)	((x)->code_min == BACKREF)
+#define IS_PARAMETER(x) ((x)->code_min == PARAMETER)
+
+
+/* A generic AST node.  All AST nodes consist of this node on the top
+   level with `obj' pointing to the actual content. */
+typedef struct {
+  tre_ast_type_t type;   /* Type of the node. */
+  void *obj;             /* Pointer to actual node. */
+  int nullable;
+  int submatch_id;
+  int num_submatches;
+  int num_tags;
+  tre_pos_and_tags_t *firstpos;
+  tre_pos_and_tags_t *lastpos;
+} tre_ast_node_t;
+
+
+/* A "literal" node.  These are created for assertions, back references,
+   tags, matching parameter settings, and all expressions that match one
+   character. */
+typedef struct {
+  long code_min;
+  long code_max;
+  int position;
+  union {
+    tre_ctype_t class;
+    int *params;
+  } u;
+  tre_ctype_t *neg_classes;
+} tre_literal_t;
+
+/* A "catenation" node.	 These are created when two regexps are concatenated.
+   If there are more than one subexpressions in sequence, the `left' part
+   holds all but the last, and `right' part holds the last subexpression
+   (catenation is left associative). */
+typedef struct {
+  tre_ast_node_t *left;
+  tre_ast_node_t *right;
+} tre_catenation_t;
+
+/* An "iteration" node.	 These are created for the "*", "+", "?", and "{m,n}"
+   operators. */
+typedef struct {
+  /* Subexpression to match. */
+  tre_ast_node_t *arg;
+  /* Minimum number of consecutive matches. */
+  int min;
+  /* Maximum number of consecutive matches. */
+  int max;
+  /* If 0, match as many characters as possible, if 1 match as few as
+     possible.	Note that this does not always mean the same thing as
+     matching as many/few repetitions as possible. */
+  unsigned int minimal:1;
+  /* Approximate matching parameters (or NULL). */
+  int *params;
+} tre_iteration_t;
+
+/* An "union" node.  These are created for the "|" operator. */
+typedef struct {
+  tre_ast_node_t *left;
+  tre_ast_node_t *right;
+} tre_union_t;
+
+tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
+
+tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
+
+tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal);
+
+tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
+
+tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+		       tre_ast_node_t *right);
+
+#ifdef TRE_DEBUG
+void
+tre_ast_print(tre_ast_node_t *tree);
+
+/* XXX - rethink AST printing API */
+void
+tre_print_params(int *params);
+#endif /* TRE_DEBUG */
+
+#endif /* TRE_AST_H */
+
+/* EOF */
+
+tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
+{
+  tre_ast_node_t *node;
+
+  node = tre_mem_calloc(mem, sizeof(*node));
+  if (!node)
+    return NULL;
+  node->obj = tre_mem_calloc(mem, size);
+  if (!node->obj)
+    return NULL;
+  node->type = type;
+  node->nullable = -1;
+  node->submatch_id = -1;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
+{
+  tre_ast_node_t *node;
+  tre_literal_t *lit;
+
+  node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t));
+  if (!node)
+    return NULL;
+  lit = node->obj;
+  lit->code_min = code_min;
+  lit->code_max = code_max;
+  lit->position = position;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+		 int minimal)
+{
+  tre_ast_node_t *node;
+  tre_iteration_t *iter;
+
+  node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t));
+  if (!node)
+    return NULL;
+  iter = node->obj;
+  iter->arg = arg;
+  iter->min = min;
+  iter->max = max;
+  iter->minimal = minimal;
+  node->num_submatches = arg->num_submatches;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+  tre_ast_node_t *node;
+
+  node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t));
+  if (node == NULL)
+    return NULL;
+  ((tre_union_t *)node->obj)->left = left;
+  ((tre_union_t *)node->obj)->right = right;
+  node->num_submatches = left->num_submatches + right->num_submatches;
+
+  return node;
+}
+
+tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+		       tre_ast_node_t *right)
+{
+  tre_ast_node_t *node;
+
+  node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t));
+  if (node == NULL)
+    return NULL;
+  ((tre_catenation_t *)node->obj)->left = left;
+  ((tre_catenation_t *)node->obj)->right = right;
+  node->num_submatches = left->num_submatches + right->num_submatches;
+
+  return node;
+}
+
+#ifdef TRE_DEBUG
+
+static void
+tre_findent(FILE *stream, int i)
+{
+  while (i-- > 0)
+    fputc(' ', stream);
+}
+
+void
+tre_print_params(int *params)
+{
+  int i;
+  if (params)
+    {
+      DPRINT(("params ["));
+      for (i = 0; i < TRE_PARAM_LAST; i++)
+	{
+	  if (params[i] == TRE_PARAM_UNSET)
+	    DPRINT(("unset"));
+	  else if (params[i] == TRE_PARAM_DEFAULT)
+	    DPRINT(("default"));
+	  else
+	    DPRINT(("%d", params[i]));
+	  if (i < TRE_PARAM_LAST - 1)
+	    DPRINT((", "));
+	}
+      DPRINT(("]"));
+    }
+}
+
+static void
+tre_do_print(FILE *stream, tre_ast_node_t *ast, int indent)
+{
+  int code_min, code_max, pos;
+  int num_tags = ast->num_tags;
+  tre_literal_t *lit;
+  tre_iteration_t *iter;
+
+  tre_findent(stream, indent);
+  switch (ast->type)
+    {
+    case LITERAL:
+      lit = ast->obj;
+      code_min = lit->code_min;
+      code_max = lit->code_max;
+      pos = lit->position;
+      if (IS_EMPTY(lit))
+	{
+	  fprintf(stream, "literal empty\n");
+	}
+      else if (IS_ASSERTION(lit))
+	{
+	  int i;
+	  char *assertions[] = { "bol", "eol", "ctype", "!ctype",
+				 "bow", "eow", "wb", "!wb" };
+	  if (code_max >= ASSERT_LAST << 1)
+	    assert(0);
+	  fprintf(stream, "assertions: ");
+	  for (i = 0; (1 << i) <= ASSERT_LAST; i++)
+	    if (code_max & (1 << i))
+	      fprintf(stream, "%s ", assertions[i]);
+	  fprintf(stream, "\n");
+	}
+      else if (IS_TAG(lit))
+	{
+	  fprintf(stream, "tag %d\n", code_max);
+	}
+      else if (IS_BACKREF(lit))
+	{
+	  fprintf(stream, "backref %d, pos %d\n", code_max, pos);
+	}
+      else if (IS_PARAMETER(lit))
+	{
+	  tre_print_params(lit->u.params);
+	  fprintf(stream, "\n");
+	}
+      else
+	{
+	  fprintf(stream, "literal (%c, %c) (%d, %d), pos %d, sub %d, "
+		  "%d tags\n", code_min, code_max, code_min, code_max, pos,
+		  ast->submatch_id, num_tags);
+	}
+      break;
+    case ITERATION:
+      iter = ast->obj;
+      fprintf(stream, "iteration {%d, %d}, sub %d, %d tags, %s\n",
+	      iter->min, iter->max, ast->submatch_id, num_tags,
+	      iter->minimal ? "minimal" : "greedy");
+      tre_do_print(stream, iter->arg, indent + 2);
+      break;
+    case UNION:
+      fprintf(stream, "union, sub %d, %d tags\n", ast->submatch_id, num_tags);
+      tre_do_print(stream, ((tre_union_t *)ast->obj)->left, indent + 2);
+      tre_do_print(stream, ((tre_union_t *)ast->obj)->right, indent + 2);
+      break;
+    case CATENATION:
+      fprintf(stream, "catenation, sub %d, %d tags\n", ast->submatch_id,
+	      num_tags);
+      tre_do_print(stream, ((tre_catenation_t *)ast->obj)->left, indent + 2);
+      tre_do_print(stream, ((tre_catenation_t *)ast->obj)->right, indent + 2);
+      break;
+    default:
+      assert(0);
+      break;
+    }
+}
+
+static void
+tre_ast_fprint(FILE *stream, tre_ast_node_t *ast)
+{
+  tre_do_print(stream, ast, 0);
+}
+
+void
+tre_ast_print(tre_ast_node_t *tree)
+{
+  printf("AST:\n");
+  tre_ast_fprint(stdout, tree);
+}
+
+#endif /* TRE_DEBUG */
+
+/* EOF */
+/*
+  tre-compile.c - TRE regex compiler
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  TODO:
+   - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive
+     function calls.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+/*
+  tre-stack.h: Stack definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+
+#ifndef TRE_STACK_H
+#define TRE_STACK_H 1
+
+
+typedef struct tre_stack_rec tre_stack_t;
+
+/* Creates a new stack object.	`size' is initial size in bytes, `max_size'
+   is maximum size, and `increment' specifies how much more space will be
+   allocated with realloc() if all space gets used up.	Returns the stack
+   object or NULL if out of memory. */
+tre_stack_t *
+tre_stack_new(int size, int max_size, int increment);
+
+/* Frees the stack object. */
+void
+tre_stack_destroy(tre_stack_t *s);
+
+/* Returns the current number of objects in the stack. */
+int
+tre_stack_num_objects(tre_stack_t *s);
+
+/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
+   `value' on top of stack `s'.  Returns REG_ESPACE if out of memory.
+   This tries to realloc() more space before failing if maximum size
+   has not yet been reached.  Returns REG_OK if successful. */
+#define declare_pushf(typetag, type)					      \
+  reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
+
+declare_pushf(voidptr, void *);
+declare_pushf(int, int);
+
+/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
+   element off of stack `s' and returns it.  The stack must not be
+   empty. */
+#define declare_popf(typetag, type)		  \
+  type tre_stack_pop_ ## typetag(tre_stack_t *s)
+
+declare_popf(voidptr, void *);
+declare_popf(int, int);
+
+/* Just to save some typing. */
+#define STACK_PUSH(s, typetag, value)					      \
+  do									      \
+    {									      \
+      status = tre_stack_push_ ## typetag(s, value);			      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#define STACK_PUSHX(s, typetag, value)					      \
+  {									      \
+    status = tre_stack_push_ ## typetag(s, value);			      \
+    if (status != REG_OK)						      \
+      break;								      \
+  }
+
+#define STACK_PUSHR(s, typetag, value)					      \
+  {									      \
+    reg_errcode_t _status;						      \
+    _status = tre_stack_push_ ## typetag(s, value);			      \
+    if (_status != REG_OK)						      \
+      return _status;							      \
+  }
+
+#endif /* TRE_STACK_H */
+
+/* EOF */
+/*
+  tre-parse.c - Regexp parser definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifndef TRE_PARSE_H
+#define TRE_PARSE_H 1
+
+/* Parse context. */
+typedef struct {
+  /* Memory allocator.	The AST is allocated using this. */
+  tre_mem_t mem;
+  /* Stack used for keeping track of regexp syntax. */
+  tre_stack_t *stack;
+  /* The parse result. */
+  tre_ast_node_t *result;
+  /* The regexp to parse and its length. */
+  const tre_char_t *re;
+  /* The first character of the entire regexp. */
+  const tre_char_t *re_start;
+  /* The first character after the end of the regexp. */
+  const tre_char_t *re_end;
+  int len;
+  /* Current submatch ID. */
+  int submatch_id;
+  /* Current position (number of literal). */
+  int position;
+  /* The highest back reference or -1 if none seen so far. */
+  int max_backref;
+  /* This flag is set if the regexp uses approximate matching. */
+  int have_approx;
+  /* Compilation flags. */
+  int cflags;
+  /* If this flag is set the top-level submatch is not captured. */
+  int nofirstsub;
+  /* The currently set approximate matching parameters. */
+  int params[TRE_PARAM_LAST];
+} tre_parse_ctx_t;
+
+/* Parses a wide character regexp pattern into a syntax tree.  This parser
+   handles both syntaxes (BRE and ERE), including the TRE extensions. */
+reg_errcode_t
+tre_parse(tre_parse_ctx_t *ctx);
+
+#endif /* TRE_PARSE_H */
+
+/* EOF */
+
+/*
+  Algorithms to setup tags so that submatch addressing can be done.
+*/
+
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+   As the left child a new tag with number `tag_id' to `node' is added,
+   and the right child is the old root. */
+static reg_errcode_t
+tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+  tre_catenation_t *c;
+
+  DPRINT(("add_tag_left: tag %d\n", tag_id));
+
+  c = tre_mem_alloc(mem, sizeof(*c));
+  if (c == NULL)
+    return REG_ESPACE;
+  c->left = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->left == NULL)
+    return REG_ESPACE;
+  c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+  if (c->right == NULL)
+    return REG_ESPACE;
+
+  c->right->obj = node->obj;
+  c->right->type = node->type;
+  c->right->nullable = -1;
+  c->right->submatch_id = -1;
+  c->right->firstpos = NULL;
+  c->right->lastpos = NULL;
+  c->right->num_tags = 0;
+  node->obj = c;
+  node->type = CATENATION;
+  return REG_OK;
+}
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+   As the right child a new tag with number `tag_id' to `node' is added,
+   and the left child is the old root. */
+static reg_errcode_t
+tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+  tre_catenation_t *c;
+
+  DPRINT(("tre_add_tag_right: tag %d\n", tag_id));
+
+  c = tre_mem_alloc(mem, sizeof(*c));
+  if (c == NULL)
+    return REG_ESPACE;
+  c->right = tre_ast_new_literal(mem, TAG, tag_id, -1);
+  if (c->right == NULL)
+    return REG_ESPACE;
+  c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+  if (c->left == NULL)
+    return REG_ESPACE;
+
+  c->left->obj = node->obj;
+  c->left->type = node->type;
+  c->left->nullable = -1;
+  c->left->submatch_id = -1;
+  c->left->firstpos = NULL;
+  c->left->lastpos = NULL;
+  c->left->num_tags = 0;
+  node->obj = c;
+  node->type = CATENATION;
+  return REG_OK;
+}
+
+typedef enum {
+  ADDTAGS_RECURSE,
+  ADDTAGS_AFTER_ITERATION,
+  ADDTAGS_AFTER_UNION_LEFT,
+  ADDTAGS_AFTER_UNION_RIGHT,
+  ADDTAGS_AFTER_CAT_LEFT,
+  ADDTAGS_AFTER_CAT_RIGHT,
+  ADDTAGS_SET_SUBMATCH_END
+} tre_addtags_symbol_t;
+
+
+typedef struct {
+  int tag;
+  int next_tag;
+} tre_tag_states_t;
+
+
+/* Go through `regset' and set submatch data for submatches that are
+   using this tag. */
+static void
+tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag)
+{
+  int i;
+
+  for (i = 0; regset[i] >= 0; i++)
+    {
+      int id = regset[i] / 2;
+      int start = !(regset[i] % 2);
+      DPRINT(("  Using tag %d for %s offset of "
+	      "submatch %d\n", tag,
+	      start ? "start" : "end", id));
+      if (start)
+	tnfa->submatch_data[id].so_tag = tag;
+      else
+	tnfa->submatch_data[id].eo_tag = tag;
+    }
+  regset[0] = -1;
+}
+
+
+/* Adds tags to appropriate locations in the parse tree in `tree', so that
+   subexpressions marked for submatch addressing can be traced. */
+static reg_errcode_t
+tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
+	     tre_tnfa_t *tnfa)
+{
+  reg_errcode_t status = REG_OK;
+  tre_addtags_symbol_t symbol;
+  tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */
+  int bottom = tre_stack_num_objects(stack);
+  /* True for first pass (counting number of needed tags) */
+  int first_pass = (mem == NULL || tnfa == NULL);
+  int *regset, *orig_regset;
+  int num_tags = 0; /* Total number of tags. */
+  int num_minimals = 0;	 /* Number of special minimal tags. */
+  int tag = 0;	    /* The tag that is to be added next. */
+  int next_tag = 1; /* Next tag to use after this one. */
+  int *parents;	    /* Stack of submatches the current submatch is
+		       contained in. */
+  int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */
+  tre_tag_states_t *saved_states;
+
+  tre_tag_direction_t direction = TRE_TAG_MINIMIZE;
+  if (!first_pass)
+    {
+      tnfa->end_tag = 0;
+      tnfa->minimal_tags[0] = -1;
+    }
+
+  regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2));
+  if (regset == NULL)
+    return REG_ESPACE;
+  regset[0] = -1;
+  orig_regset = regset;
+
+  parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1));
+  if (parents == NULL)
+    {
+      xfree(regset);
+      return REG_ESPACE;
+    }
+  parents[0] = -1;
+
+  saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1));
+  if (saved_states == NULL)
+    {
+      xfree(regset);
+      xfree(parents);
+      return REG_ESPACE;
+    }
+  else
+    {
+      unsigned int i;
+      for (i = 0; i <= tnfa->num_submatches; i++)
+	saved_states[i].tag = -1;
+    }
+
+  STACK_PUSH(stack, voidptr, node);
+  STACK_PUSH(stack, int, ADDTAGS_RECURSE);
+
+  while (tre_stack_num_objects(stack) > bottom)
+    {
+      if (status != REG_OK)
+	break;
+
+      symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
+      switch (symbol)
+	{
+
+	case ADDTAGS_SET_SUBMATCH_END:
+	  {
+	    int id = tre_stack_pop_int(stack);
+	    int i;
+
+	    /* Add end of this submatch to regset. */
+	    for (i = 0; regset[i] >= 0; i++);
+	    regset[i] = id * 2 + 1;
+	    regset[i + 1] = -1;
+
+	    /* Pop this submatch from the parents stack. */
+	    for (i = 0; parents[i] >= 0; i++);
+	    parents[i - 1] = -1;
+	    break;
+	  }
+
+	case ADDTAGS_RECURSE:
+	  node = tre_stack_pop_voidptr(stack);
+
+	  if (node->submatch_id >= 0)
+	    {
+	      int id = node->submatch_id;
+	      int i;
+
+
+	      /* Add start of this submatch to regset. */
+	      for (i = 0; regset[i] >= 0; i++);
+	      regset[i] = id * 2;
+	      regset[i + 1] = -1;
+
+	      if (!first_pass)
+		{
+		  for (i = 0; parents[i] >= 0; i++);
+		  tnfa->submatch_data[id].parents = NULL;
+		  if (i > 0)
+		    {
+		      int *p = xmalloc(sizeof(*p) * (i + 1));
+		      if (p == NULL)
+			{
+			  status = REG_ESPACE;
+			  break;
+			}
+		      assert(tnfa->submatch_data[id].parents == NULL);
+		      tnfa->submatch_data[id].parents = p;
+		      for (i = 0; parents[i] >= 0; i++)
+			p[i] = parents[i];
+		      p[i] = -1;
+		    }
+		}
+
+	      /* Add end of this submatch to regset after processing this
+		 node. */
+	      STACK_PUSHX(stack, int, node->submatch_id);
+	      STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END);
+	    }
+
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = node->obj;
+
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+		  {
+		    int i;
+		    DPRINT(("Literal %d-%d\n",
+			    (int)lit->code_min, (int)lit->code_max));
+		    if (regset[0] >= 0)
+		      {
+			/* Regset is not empty, so add a tag before the
+			   literal or backref. */
+			if (!first_pass)
+			  {
+			    status = tre_add_tag_left(mem, node, tag);
+			    tnfa->tag_directions[tag] = direction;
+			    if (minimal_tag >= 0)
+			      {
+				DPRINT(("Minimal %d, %d\n", minimal_tag, tag));
+				for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+				tnfa->minimal_tags[i] = tag;
+				tnfa->minimal_tags[i + 1] = minimal_tag;
+				tnfa->minimal_tags[i + 2] = -1;
+				minimal_tag = -1;
+				num_minimals++;
+			      }
+			    tre_purge_regset(regset, tnfa, tag);
+			  }
+			else
+			  {
+			    DPRINT(("  num_tags = 1\n"));
+			    node->num_tags = 1;
+			  }
+
+			DPRINT(("  num_tags++\n"));
+			regset[0] = -1;
+			tag = next_tag;
+			num_tags++;
+			next_tag++;
+		      }
+		  }
+		else
+		  {
+		    assert(!IS_TAG(lit));
+		  }
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		tre_ast_node_t *left = cat->left;
+		tre_ast_node_t *right = cat->right;
+		int reserved_tag = -1;
+		DPRINT(("Catenation, next_tag = %d\n", next_tag));
+
+
+		/* After processing right child. */
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT);
+
+		/* Process right child. */
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* After processing left child. */
+		STACK_PUSHX(stack, int, next_tag + left->num_tags);
+		DPRINT(("  Pushing %d for after left\n",
+			next_tag + left->num_tags));
+		if (left->num_tags > 0 && right->num_tags > 0)
+		  {
+		    /* Reserve the next tag to the right child. */
+		    DPRINT(("  Reserving next_tag %d to right child\n",
+			    next_tag));
+		    reserved_tag = next_tag;
+		    next_tag++;
+		  }
+		STACK_PUSHX(stack, int, reserved_tag);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT);
+
+		/* Process left child. */
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		}
+	      break;
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		DPRINT(("Iteration\n"));
+
+		if (first_pass)
+		  {
+		    STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal);
+		  }
+		else
+		  {
+		    STACK_PUSHX(stack, int, tag);
+		    STACK_PUSHX(stack, int, iter->minimal);
+		  }
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION);
+
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* Regset is not empty, so add a tag here. */
+		if (regset[0] >= 0 || iter->minimal)
+		  {
+		    if (!first_pass)
+		      {
+			int i;
+			status = tre_add_tag_left(mem, node, tag);
+			if (iter->minimal)
+			  tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
+			else
+			  tnfa->tag_directions[tag] = direction;
+			if (minimal_tag >= 0)
+			  {
+			    DPRINT(("Minimal %d, %d\n", minimal_tag, tag));
+			    for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+			    tnfa->minimal_tags[i] = tag;
+			    tnfa->minimal_tags[i + 1] = minimal_tag;
+			    tnfa->minimal_tags[i + 2] = -1;
+			    minimal_tag = -1;
+			    num_minimals++;
+			  }
+			tre_purge_regset(regset, tnfa, tag);
+		      }
+
+		    DPRINT(("  num_tags++\n"));
+		    regset[0] = -1;
+		    tag = next_tag;
+		    num_tags++;
+		    next_tag++;
+		  }
+		direction = TRE_TAG_MINIMIZE;
+	      }
+	      break;
+	    case UNION:
+	      {
+		tre_union_t *uni = node->obj;
+		tre_ast_node_t *left = uni->left;
+		tre_ast_node_t *right = uni->right;
+		int left_tag;
+		int right_tag;
+
+		if (regset[0] >= 0)
+		  {
+		    left_tag = next_tag;
+		    right_tag = next_tag + 1;
+		  }
+		else
+		  {
+		    left_tag = tag;
+		    right_tag = next_tag;
+		  }
+
+		DPRINT(("Union\n"));
+
+		/* After processing right child. */
+		STACK_PUSHX(stack, int, right_tag);
+		STACK_PUSHX(stack, int, left_tag);
+		STACK_PUSHX(stack, voidptr, regset);
+		STACK_PUSHX(stack, int, regset[0] >= 0);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT);
+
+		/* Process right child. */
+		STACK_PUSHX(stack, voidptr, right);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* After processing left child. */
+		STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT);
+
+		/* Process left child. */
+		STACK_PUSHX(stack, voidptr, left);
+		STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+		/* Regset is not empty, so add a tag here. */
+		if (regset[0] >= 0)
+		  {
+		    if (!first_pass)
+		      {
+			int i;
+			status = tre_add_tag_left(mem, node, tag);
+			tnfa->tag_directions[tag] = direction;
+			if (minimal_tag >= 0)
+			  {
+			    DPRINT(("Minimal %d, %d\n", minimal_tag, tag));
+			    for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+			    tnfa->minimal_tags[i] = tag;
+			    tnfa->minimal_tags[i + 1] = minimal_tag;
+			    tnfa->minimal_tags[i + 2] = -1;
+			    minimal_tag = -1;
+			    num_minimals++;
+			  }
+			tre_purge_regset(regset, tnfa, tag);
+		      }
+
+		    DPRINT(("  num_tags++\n"));
+		    regset[0] = -1;
+		    tag = next_tag;
+		    num_tags++;
+		    next_tag++;
+		  }
+
+		if (node->num_submatches > 0)
+		  {
+		    /* The next two tags are reserved for markers. */
+		    next_tag++;
+		    tag = next_tag;
+		    next_tag++;
+		  }
+
+		break;
+	      }
+	    }
+
+	  if (node->submatch_id >= 0)
+	    {
+	      int i;
+	      /* Push this submatch on the parents stack. */
+	      for (i = 0; parents[i] >= 0; i++);
+	      parents[i] = node->submatch_id;
+	      parents[i + 1] = -1;
+	    }
+
+	  break; /* end case: ADDTAGS_RECURSE */
+
+	case ADDTAGS_AFTER_ITERATION:
+	  {
+	    int minimal = 0;
+	    int enter_tag;
+	    node = tre_stack_pop_voidptr(stack);
+	    if (first_pass)
+	      {
+		node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags
+		  + tre_stack_pop_int(stack);
+		minimal_tag = -1;
+	      }
+	    else
+	      {
+		minimal = tre_stack_pop_int(stack);
+		enter_tag = tre_stack_pop_int(stack);
+		if (minimal)
+		  minimal_tag = enter_tag;
+	      }
+
+	    DPRINT(("After iteration\n"));
+	    if (!first_pass)
+	      {
+		DPRINT(("  Setting direction to %s\n",
+			minimal ? "minimize" : "maximize"));
+		if (minimal)
+		  direction = TRE_TAG_MINIMIZE;
+		else
+		  direction = TRE_TAG_MAXIMIZE;
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_CAT_LEFT:
+	  {
+	    int new_tag = tre_stack_pop_int(stack);
+	    next_tag = tre_stack_pop_int(stack);
+	    DPRINT(("After cat left, tag = %d, next_tag = %d\n",
+		    tag, next_tag));
+	    if (new_tag >= 0)
+	      {
+		DPRINT(("  Setting tag to %d\n", new_tag));
+		tag = new_tag;
+	      }
+	    break;
+	  }
+
+	case ADDTAGS_AFTER_CAT_RIGHT:
+	  DPRINT(("After cat right\n"));
+	  node = tre_stack_pop_voidptr(stack);
+	  if (first_pass)
+	    node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags
+	      + ((tre_catenation_t *)node->obj)->right->num_tags;
+	  break;
+
+	case ADDTAGS_AFTER_UNION_LEFT:
+	  DPRINT(("After union left\n"));
+	  /* Lift the bottom of the `regset' array so that when processing
+	     the right operand the items currently in the array are
+	     invisible.	 The original bottom was saved at ADDTAGS_UNION and
+	     will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */
+	  while (*regset >= 0)
+	    regset++;
+	  break;
+
+	case ADDTAGS_AFTER_UNION_RIGHT:
+	  {
+	    int added_tags, tag_left, tag_right;
+	    tre_ast_node_t *left = tre_stack_pop_voidptr(stack);
+	    tre_ast_node_t *right = tre_stack_pop_voidptr(stack);
+	    DPRINT(("After union right\n"));
+	    node = tre_stack_pop_voidptr(stack);
+	    added_tags = tre_stack_pop_int(stack);
+	    if (first_pass)
+	      {
+		node->num_tags = ((tre_union_t *)node->obj)->left->num_tags
+		  + ((tre_union_t *)node->obj)->right->num_tags + added_tags
+		  + ((node->num_submatches > 0) ? 2 : 0);
+	      }
+	    regset = tre_stack_pop_voidptr(stack);
+	    tag_left = tre_stack_pop_int(stack);
+	    tag_right = tre_stack_pop_int(stack);
+
+	    /* Add tags after both children, the left child gets a smaller
+	       tag than the right child.  This guarantees that we prefer
+	       the left child over the right child. */
+	    /* XXX - This is not always necessary (if the children have
+	       tags which must be seen for every match of that child). */
+	    /* XXX - Check if this is the only place where tre_add_tag_right
+	       is used.	 If so, use tre_add_tag_left (putting the tag before
+	       the child as opposed after the child) and throw away
+	       tre_add_tag_right. */
+	    if (node->num_submatches > 0)
+	      {
+		if (!first_pass)
+		  {
+		    status = tre_add_tag_right(mem, left, tag_left);
+		    tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE;
+		    status = tre_add_tag_right(mem, right, tag_right);
+		    tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE;
+		  }
+		DPRINT(("  num_tags += 2\n"));
+		num_tags += 2;
+	      }
+	    direction = TRE_TAG_MAXIMIZE;
+	    break;
+	  }
+
+	default:
+	  assert(0);
+	  break;
+
+	} /* end switch(symbol) */
+    } /* end while(tre_stack_num_objects(stack) > bottom) */
+
+  if (!first_pass)
+    tre_purge_regset(regset, tnfa, tag);
+
+  if (!first_pass && minimal_tag >= 0)
+    {
+      int i;
+      DPRINT(("Minimal %d, %d\n", minimal_tag, tag));
+      for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+      tnfa->minimal_tags[i] = tag;
+      tnfa->minimal_tags[i + 1] = minimal_tag;
+      tnfa->minimal_tags[i + 2] = -1;
+      minimal_tag = -1;
+      num_minimals++;
+    }
+
+  DPRINT(("tre_add_tags: %s complete.  Number of tags %d.\n",
+	  first_pass? "First pass" : "Second pass", num_tags));
+
+  assert(tree->num_tags == num_tags);
+  tnfa->end_tag = num_tags;
+  tnfa->num_tags = num_tags;
+  tnfa->num_minimals = num_minimals;
+  xfree(orig_regset);
+  xfree(parents);
+  xfree(saved_states);
+  return status;
+}
+
+
+
+/*
+  AST to TNFA compilation routines.
+*/
+
+typedef enum {
+  COPY_RECURSE,
+  COPY_SET_RESULT_PTR
+} tre_copyast_symbol_t;
+
+/* Flags for tre_copy_ast(). */
+#define COPY_REMOVE_TAGS	 1
+#define COPY_MAXIMIZE_FIRST_TAG	 2
+
+static reg_errcode_t
+tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+	     int flags, int *pos_add, tre_tag_direction_t *tag_directions,
+	     tre_ast_node_t **copy, int *max_pos)
+{
+  reg_errcode_t status = REG_OK;
+  int bottom = tre_stack_num_objects(stack);
+  int num_copied = 0;
+  int first_tag = 1;
+  tre_ast_node_t **result = copy;
+  tre_copyast_symbol_t symbol;
+
+  STACK_PUSH(stack, voidptr, ast);
+  STACK_PUSH(stack, int, COPY_RECURSE);
+
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      tre_ast_node_t *node;
+      if (status != REG_OK)
+	break;
+
+      symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack);
+      switch (symbol)
+	{
+	case COPY_SET_RESULT_PTR:
+	  result = tre_stack_pop_voidptr(stack);
+	  break;
+	case COPY_RECURSE:
+	  node = tre_stack_pop_voidptr(stack);
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = node->obj;
+		int pos = lit->position;
+		int min = lit->code_min;
+		int max = lit->code_max;
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+		  {
+		    /* XXX - e.g. [ab] has only one position but two
+		       nodes, so we are creating holes in the state space
+		       here.  Not fatal, just wastes memory. */
+		    pos += *pos_add;
+		    num_copied++;
+		  }
+		else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS))
+		  {
+		    /* Change this tag to empty. */
+		    min = EMPTY;
+		    max = pos = -1;
+		  }
+		else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG)
+			 && first_tag)
+		  {
+		    /* Maximize the first tag. */
+		    tag_directions[max] = TRE_TAG_MAXIMIZE;
+		    first_tag = 0;
+		  }
+		*result = tre_ast_new_literal(mem, min, max, pos);
+		if (*result == NULL)
+		  status = REG_ESPACE;
+
+		if (pos > *max_pos)
+		  *max_pos = pos;
+		break;
+	      }
+	    case UNION:
+	      {
+		tre_union_t *uni = node->obj;
+		tre_union_t *tmp;
+		*result = tre_ast_new_union(mem, uni->left, uni->right);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		tmp = (*result)->obj;
+		result = &tmp->left;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		tre_catenation_t *tmp;
+		*result = tre_ast_new_catenation(mem, cat->left, cat->right);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		tmp = (*result)->obj;
+		tmp->left = NULL;
+		tmp->right = NULL;
+		result = &tmp->left;
+
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		STACK_PUSHX(stack, voidptr, &tmp->right);
+		STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		break;
+	      }
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, COPY_RECURSE);
+		*result = tre_ast_new_iter(mem, iter->arg, iter->min,
+					   iter->max, iter->minimal);
+		if (*result == NULL)
+		  {
+		    status = REG_ESPACE;
+		    break;
+		  }
+		iter = (*result)->obj;
+		result = &iter->arg;
+		break;
+	      }
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+	}
+    }
+  *pos_add += num_copied;
+  return status;
+}
+
+typedef enum {
+  EXPAND_RECURSE,
+  EXPAND_AFTER_ITER
+} tre_expand_ast_symbol_t;
+
+/* Expands each iteration node that has a finite nonzero minimum or maximum
+   iteration count to a catenated sequence of copies of the node. */
+static reg_errcode_t
+tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+	       int *position, tre_tag_direction_t *tag_directions,
+	       int *max_depth)
+{
+  reg_errcode_t status = REG_OK;
+  int bottom = tre_stack_num_objects(stack);
+  int pos_add = 0;
+  int pos_add_total = 0;
+  int max_pos = 0;
+  /* Current approximate matching parameters. */
+  int params[TRE_PARAM_LAST];
+  /* Approximate parameter nesting level. */
+  int params_depth = 0;
+  int iter_depth = 0;
+  int i;
+
+  for (i = 0; i < TRE_PARAM_LAST; i++)
+    params[i] = TRE_PARAM_DEFAULT;
+
+  STACK_PUSHR(stack, voidptr, ast);
+  STACK_PUSHR(stack, int, EXPAND_RECURSE);
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      tre_ast_node_t *node;
+      tre_expand_ast_symbol_t symbol;
+
+      if (status != REG_OK)
+	break;
+
+      DPRINT(("pos_add %d\n", pos_add));
+
+      symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
+      switch (symbol)
+	{
+	case EXPAND_RECURSE:
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit= node->obj;
+		if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+		  {
+		    lit->position += pos_add;
+		    if (lit->position > max_pos)
+		      max_pos = lit->position;
+		  }
+		break;
+	      }
+	    case UNION:
+	      {
+		tre_union_t *uni = node->obj;
+		STACK_PUSHX(stack, voidptr, uni->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, uni->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		break;
+	      }
+	    case CATENATION:
+	      {
+		tre_catenation_t *cat = node->obj;
+		STACK_PUSHX(stack, voidptr, cat->right);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		STACK_PUSHX(stack, voidptr, cat->left);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		break;
+	      }
+	    case ITERATION:
+	      {
+		tre_iteration_t *iter = node->obj;
+		STACK_PUSHX(stack, int, pos_add);
+		STACK_PUSHX(stack, voidptr, node);
+		STACK_PUSHX(stack, int, EXPAND_AFTER_ITER);
+		STACK_PUSHX(stack, voidptr, iter->arg);
+		STACK_PUSHX(stack, int, EXPAND_RECURSE);
+		/* If we are going to expand this node at EXPAND_AFTER_ITER
+		   then don't increase the `pos' fields of the nodes now, it
+		   will get done when expanding. */
+		if (iter->min > 1 || iter->max > 1)
+		  pos_add = 0;
+		iter_depth++;
+		DPRINT(("iter\n"));
+		break;
+	      }
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+	case EXPAND_AFTER_ITER:
+	  {
+	    tre_iteration_t *iter = node->obj;
+	    int pos_add_last;
+	    pos_add = tre_stack_pop_int(stack);
+	    pos_add_last = pos_add;
+	    if (iter->min > 1 || iter->max > 1)
+	      {
+		tre_ast_node_t *seq1 = NULL, *seq2 = NULL;
+		int j;
+		int pos_add_save = pos_add;
+
+		/* Create a catenated sequence of copies of the node. */
+		for (j = 0; j < iter->min; j++)
+		  {
+		    tre_ast_node_t *copy;
+		    /* Remove tags from all but the last copy. */
+		    int flags = ((j + 1 < iter->min)
+				 ? COPY_REMOVE_TAGS
+				 : COPY_MAXIMIZE_FIRST_TAG);
+		    DPRINT(("  pos_add %d\n", pos_add));
+		    pos_add_save = pos_add;
+		    status = tre_copy_ast(mem, stack, iter->arg, flags,
+					  &pos_add, tag_directions, &copy,
+					  &max_pos);
+		    if (status != REG_OK)
+		      return status;
+		    if (seq1 != NULL)
+		      seq1 = tre_ast_new_catenation(mem, seq1, copy);
+		    else
+		      seq1 = copy;
+		    if (seq1 == NULL)
+		      return REG_ESPACE;
+		  }
+
+		if (iter->max == -1)
+		  {
+		    /* No upper limit. */
+		    pos_add_save = pos_add;
+		    status = tre_copy_ast(mem, stack, iter->arg, 0,
+					  &pos_add, NULL, &seq2, &max_pos);
+		    if (status != REG_OK)
+		      return status;
+		    seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0);
+		    if (seq2 == NULL)
+		      return REG_ESPACE;
+		  }
+		else
+		  {
+		    for (j = iter->min; j < iter->max; j++)
+		      {
+			tre_ast_node_t *tmp, *copy;
+			pos_add_save = pos_add;
+			status = tre_copy_ast(mem, stack, iter->arg, 0,
+					      &pos_add, NULL, &copy, &max_pos);
+			if (status != REG_OK)
+			  return status;
+			if (seq2 != NULL)
+			  seq2 = tre_ast_new_catenation(mem, copy, seq2);
+			else
+			  seq2 = copy;
+			if (seq2 == NULL)
+			  return REG_ESPACE;
+			tmp = tre_ast_new_literal(mem, EMPTY, -1, -1);
+			if (tmp == NULL)
+			  return REG_ESPACE;
+			seq2 = tre_ast_new_union(mem, tmp, seq2);
+			if (seq2 == NULL)
+			  return REG_ESPACE;
+		      }
+		  }
+
+		pos_add = pos_add_save;
+		if (seq1 == NULL)
+		  seq1 = seq2;
+		else if (seq2 != NULL)
+		  seq1 = tre_ast_new_catenation(mem, seq1, seq2);
+		if (seq1 == NULL)
+		  return REG_ESPACE;
+		node->obj = seq1->obj;
+		node->type = seq1->type;
+	      }
+
+	    iter_depth--;
+	    pos_add_total += pos_add - pos_add_last;
+	    if (iter_depth == 0)
+	      pos_add = pos_add_total;
+
+	    /* If approximate parameters are specified, surround the result
+	       with two parameter setting nodes.  The one on the left sets
+	       the specified parameters, and the one on the right restores
+	       the old parameters. */
+	    if (iter->params)
+	      {
+		tre_ast_node_t *tmp_l, *tmp_r, *tmp_node, *node_copy;
+		int *old_params;
+
+		tmp_l = tre_ast_new_literal(mem, PARAMETER, 0, -1);
+		if (!tmp_l)
+		  return REG_ESPACE;
+		((tre_literal_t *)tmp_l->obj)->u.params = iter->params;
+		iter->params[TRE_PARAM_DEPTH] = params_depth + 1;
+		tmp_r = tre_ast_new_literal(mem, PARAMETER, 0, -1);
+		if (!tmp_r)
+		  return REG_ESPACE;
+		old_params = tre_mem_alloc(mem, sizeof(*old_params)
+					   * TRE_PARAM_LAST);
+		if (!old_params)
+		  return REG_ESPACE;
+		for (i = 0; i < TRE_PARAM_LAST; i++)
+		  old_params[i] = params[i];
+		((tre_literal_t *)tmp_r->obj)->u.params = old_params;
+		old_params[TRE_PARAM_DEPTH] = params_depth;
+		/* XXX - this is the only place where ast_new_node is
+		   needed -- should be moved inside AST module. */
+		node_copy = tre_ast_new_node(mem, ITERATION,
+					     sizeof(tre_iteration_t));
+		if (!node_copy)
+		  return REG_ESPACE;
+		node_copy->obj = node->obj;
+		tmp_node = tre_ast_new_catenation(mem, tmp_l, node_copy);
+		if (!tmp_node)
+		  return REG_ESPACE;
+		tmp_node = tre_ast_new_catenation(mem, tmp_node, tmp_r);
+		if (!tmp_node)
+		  return REG_ESPACE;
+		/* Replace the contents of `node' with `tmp_node'. */
+		memcpy(node, tmp_node, sizeof(*node));
+		node->obj = tmp_node->obj;
+		node->type = tmp_node->type;
+		params_depth++;
+		if (params_depth > *max_depth)
+		  *max_depth = params_depth;
+	      }
+	    break;
+	  }
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  *position += pos_add_total;
+
+  /* `max_pos' should never be larger than `*position' if the above
+     code works, but just an extra safeguard let's make sure
+     `*position' is set large enough so enough memory will be
+     allocated for the transition table. */
+  if (max_pos > *position)
+    *position = max_pos;
+
+#ifdef TRE_DEBUG
+  DPRINT(("Expanded AST:\n"));
+  tre_ast_print(ast);
+  DPRINT(("*position %d, max_pos %d\n", *position, max_pos));
+#endif
+
+  return status;
+}
+
+static tre_pos_and_tags_t *
+tre_set_empty(tre_mem_t mem)
+{
+  tre_pos_and_tags_t *new_set;
+
+  new_set = tre_mem_calloc(mem, sizeof(*new_set));
+  if (new_set == NULL)
+    return NULL;
+
+  new_set[0].position = -1;
+  new_set[0].code_min = -1;
+  new_set[0].code_max = -1;
+
+  return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_one(tre_mem_t mem, int position, int code_min, int code_max,
+	    tre_ctype_t class, tre_ctype_t *neg_classes, int backref)
+{
+  tre_pos_and_tags_t *new_set;
+
+  new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2);
+  if (new_set == NULL)
+    return NULL;
+
+  new_set[0].position = position;
+  new_set[0].code_min = code_min;
+  new_set[0].code_max = code_max;
+  new_set[0].class = class;
+  new_set[0].neg_classes = neg_classes;
+  new_set[0].backref = backref;
+  new_set[1].position = -1;
+  new_set[1].code_min = -1;
+  new_set[1].code_max = -1;
+
+  return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2,
+	      int *tags, int assertions, int *params)
+{
+  int s1, s2, i, j;
+  tre_pos_and_tags_t *new_set;
+  int *new_tags;
+  int num_tags;
+
+  for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++);
+  for (s1 = 0; set1[s1].position >= 0; s1++);
+  for (s2 = 0; set2[s2].position >= 0; s2++);
+  new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1));
+  if (!new_set )
+    return NULL;
+
+  for (s1 = 0; set1[s1].position >= 0; s1++)
+    {
+      new_set[s1].position = set1[s1].position;
+      new_set[s1].code_min = set1[s1].code_min;
+      new_set[s1].code_max = set1[s1].code_max;
+      new_set[s1].assertions = set1[s1].assertions | assertions;
+      new_set[s1].class = set1[s1].class;
+      new_set[s1].neg_classes = set1[s1].neg_classes;
+      new_set[s1].backref = set1[s1].backref;
+      if (set1[s1].tags == NULL && tags == NULL)
+	new_set[s1].tags = NULL;
+      else
+	{
+	  for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++);
+	  new_tags = tre_mem_alloc(mem, (sizeof(*new_tags)
+					 * (i + num_tags + 1)));
+	  if (new_tags == NULL)
+	    return NULL;
+	  for (j = 0; j < i; j++)
+	    new_tags[j] = set1[s1].tags[j];
+	  for (i = 0; i < num_tags; i++)
+	    new_tags[j + i] = tags[i];
+	  new_tags[j + i] = -1;
+	  new_set[s1].tags = new_tags;
+	}
+      if (set1[s1].params)
+	new_set[s1].params = set1[s1].params;
+      if (params)
+	{
+	  if (!new_set[s1].params)
+	    new_set[s1].params = params;
+	  else
+	    {
+	      new_set[s1].params = tre_mem_alloc(mem, sizeof(*params) *
+						 TRE_PARAM_LAST);
+	      if (!new_set[s1].params)
+		return NULL;
+	      for (i = 0; i < TRE_PARAM_LAST; i++)
+		if (params[i] != TRE_PARAM_UNSET)
+		  new_set[s1].params[i] = params[i];
+	    }
+	}
+    }
+
+  for (s2 = 0; set2[s2].position >= 0; s2++)
+    {
+      new_set[s1 + s2].position = set2[s2].position;
+      new_set[s1 + s2].code_min = set2[s2].code_min;
+      new_set[s1 + s2].code_max = set2[s2].code_max;
+      /* XXX - why not | assertions here as well? */
+      new_set[s1 + s2].assertions = set2[s2].assertions;
+      new_set[s1 + s2].class = set2[s2].class;
+      new_set[s1 + s2].neg_classes = set2[s2].neg_classes;
+      new_set[s1 + s2].backref = set2[s2].backref;
+      if (set2[s2].tags == NULL)
+	new_set[s1 + s2].tags = NULL;
+      else
+	{
+	  for (i = 0; set2[s2].tags[i] >= 0; i++);
+	  new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1));
+	  if (new_tags == NULL)
+	    return NULL;
+	  for (j = 0; j < i; j++)
+	    new_tags[j] = set2[s2].tags[j];
+	  new_tags[j] = -1;
+	  new_set[s1 + s2].tags = new_tags;
+	}
+      if (set2[s2].params)
+	new_set[s1 + s2].params = set2[s2].params;
+      if (params)
+	{
+	  if (!new_set[s1 + s2].params)
+	    new_set[s1 + s2].params = params;
+	  else
+	    {
+	      new_set[s1 + s2].params = tre_mem_alloc(mem, sizeof(*params) *
+						      TRE_PARAM_LAST);
+	      if (!new_set[s1 + s2].params)
+		return NULL;
+	      for (i = 0; i < TRE_PARAM_LAST; i++)
+		if (params[i] != TRE_PARAM_UNSET)
+		  new_set[s1 + s2].params[i] = params[i];
+	    }
+	}
+    }
+  new_set[s1 + s2].position = -1;
+  return new_set;
+}
+
+/* Finds the empty path through `node' which is the one that should be
+   taken according to POSIX.2 rules, and adds the tags on that path to
+   `tags'.   `tags' may be NULL.  If `num_tags_seen' is not NULL, it is
+   set to the number of tags seen on the path. */
+static reg_errcode_t
+tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
+		int *assertions, int *params, int *num_tags_seen,
+		int *params_seen)
+{
+  tre_literal_t *lit;
+  tre_union_t *uni;
+  tre_catenation_t *cat;
+  tre_iteration_t *iter;
+  int i;
+  int bottom = tre_stack_num_objects(stack);
+  reg_errcode_t status = REG_OK;
+  if (num_tags_seen)
+    *num_tags_seen = 0;
+  if (params_seen)
+    *params_seen = 0;
+
+  status = tre_stack_push_voidptr(stack, node);
+
+  /* Walk through the tree recursively. */
+  while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+    {
+      node = tre_stack_pop_voidptr(stack);
+
+      switch (node->type)
+	{
+	case LITERAL:
+	  lit = (tre_literal_t *)node->obj;
+	  switch (lit->code_min)
+	    {
+	    case TAG:
+	      if (lit->code_max >= 0)
+		{
+		  if (tags != NULL)
+		    {
+		      /* Add the tag to `tags'. */
+		      for (i = 0; tags[i] >= 0; i++)
+			if (tags[i] == lit->code_max)
+			  break;
+		      if (tags[i] < 0)
+			{
+			  tags[i] = lit->code_max;
+			  tags[i + 1] = -1;
+			}
+		    }
+		  if (num_tags_seen)
+		    (*num_tags_seen)++;
+		}
+	      break;
+	    case ASSERTION:
+	      assert(lit->code_max >= 1
+		     || lit->code_max <= ASSERT_LAST);
+	      if (assertions != NULL)
+		*assertions |= lit->code_max;
+	      break;
+	    case PARAMETER:
+	      if (params != NULL)
+		for (i = 0; i < TRE_PARAM_LAST; i++)
+		  params[i] = lit->u.params[i];
+	      if (params_seen != NULL)
+		*params_seen = 1;
+	      break;
+	    case EMPTY:
+	      break;
+	    default:
+	      assert(0);
+	      break;
+	    }
+	  break;
+
+	case UNION:
+	  /* Subexpressions starting earlier take priority over ones
+	     starting later, so we prefer the left subexpression over the
+	     right subexpression. */
+	  uni = (tre_union_t *)node->obj;
+	  if (uni->left->nullable)
+	    STACK_PUSHX(stack, voidptr, uni->left)
+	  else if (uni->right->nullable)
+	    STACK_PUSHX(stack, voidptr, uni->right)
+	  else
+	    assert(0);
+	  break;
+
+	case CATENATION:
+	  /* The path must go through both children. */
+	  cat = (tre_catenation_t *)node->obj;
+	  assert(cat->left->nullable);
+	  assert(cat->right->nullable);
+	  STACK_PUSHX(stack, voidptr, cat->left);
+	  STACK_PUSHX(stack, voidptr, cat->right);
+	  break;
+
+	case ITERATION:
+	  /* A match with an empty string is preferred over no match at
+	     all, so we go through the argument if possible. */
+	  iter = (tre_iteration_t *)node->obj;
+	  if (iter->arg->nullable)
+	    STACK_PUSHX(stack, voidptr, iter->arg);
+	  break;
+
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  return status;
+}
+
+
+typedef enum {
+  NFL_RECURSE,
+  NFL_POST_UNION,
+  NFL_POST_CATENATION,
+  NFL_POST_ITERATION
+} tre_nfl_stack_symbol_t;
+
+
+/* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for
+   the nodes of the AST `tree'. */
+static reg_errcode_t
+tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
+{
+  int bottom = tre_stack_num_objects(stack);
+
+  STACK_PUSHR(stack, voidptr, tree);
+  STACK_PUSHR(stack, int, NFL_RECURSE);
+
+  while (tre_stack_num_objects(stack) > bottom)
+    {
+      tre_nfl_stack_symbol_t symbol;
+      tre_ast_node_t *node;
+
+      symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack);
+      node = tre_stack_pop_voidptr(stack);
+      switch (symbol)
+	{
+	case NFL_RECURSE:
+	  switch (node->type)
+	    {
+	    case LITERAL:
+	      {
+		tre_literal_t *lit = (tre_literal_t *)node->obj;
+		if (IS_BACKREF(lit))
+		  {
+		    /* Back references: nullable = false, firstpos = {i},
+		       lastpos = {i}. */
+		    node->nullable = 0;
+		    node->firstpos = tre_set_one(mem, lit->position, 0,
+					     TRE_CHAR_MAX, 0, NULL, -1);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_one(mem, lit->position, 0,
+						TRE_CHAR_MAX, 0, NULL,
+						(int)lit->code_max);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		else if (lit->code_min < 0)
+		  {
+		    /* Tags, empty strings, params, and zero width assertions:
+		       nullable = true, firstpos = {}, and lastpos = {}. */
+		    node->nullable = 1;
+		    node->firstpos = tre_set_empty(mem);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_empty(mem);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		else
+		  {
+		    /* Literal at position i: nullable = false, firstpos = {i},
+		       lastpos = {i}. */
+		    node->nullable = 0;
+		    node->firstpos =
+		      tre_set_one(mem, lit->position, (int)lit->code_min,
+				  (int)lit->code_max, 0, NULL, -1);
+		    if (!node->firstpos)
+		      return REG_ESPACE;
+		    node->lastpos = tre_set_one(mem, lit->position,
+						(int)lit->code_min,
+						(int)lit->code_max,
+						lit->u.class, lit->neg_classes,
+						-1);
+		    if (!node->lastpos)
+		      return REG_ESPACE;
+		  }
+		break;
+	      }
+
+	    case UNION:
+	      /* Compute the attributes for the two subtrees, and after that
+		 for this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_UNION);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+
+	    case CATENATION:
+	      /* Compute the attributes for the two subtrees, and after that
+		 for this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_CATENATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+
+	    case ITERATION:
+	      /* Compute the attributes for the subtree, and after that for
+		 this node. */
+	      STACK_PUSHR(stack, voidptr, node);
+	      STACK_PUSHR(stack, int, NFL_POST_ITERATION);
+	      STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg);
+	      STACK_PUSHR(stack, int, NFL_RECURSE);
+	      break;
+	    }
+	  break; /* end case: NFL_RECURSE */
+
+	case NFL_POST_UNION:
+	  {
+	    tre_union_t *uni = (tre_union_t *)node->obj;
+	    node->nullable = uni->left->nullable || uni->right->nullable;
+	    node->firstpos = tre_set_union(mem, uni->left->firstpos,
+					   uni->right->firstpos, NULL, 0, NULL);
+	    if (!node->firstpos)
+	      return REG_ESPACE;
+	    node->lastpos = tre_set_union(mem, uni->left->lastpos,
+					  uni->right->lastpos, NULL, 0, NULL);
+	    if (!node->lastpos)
+	      return REG_ESPACE;
+	    break;
+	  }
+
+	case NFL_POST_ITERATION:
+	  {
+	    tre_iteration_t *iter = (tre_iteration_t *)node->obj;
+
+	    if (iter->min == 0 || iter->arg->nullable)
+	      node->nullable = 1;
+	    else
+	      node->nullable = 0;
+	    node->firstpos = iter->arg->firstpos;
+	    node->lastpos = iter->arg->lastpos;
+	    break;
+	  }
+
+	case NFL_POST_CATENATION:
+	  {
+	    int num_tags, *tags, assertions, params_seen;
+	    int *params;
+	    reg_errcode_t status;
+	    tre_catenation_t *cat = node->obj;
+	    node->nullable = cat->left->nullable && cat->right->nullable;
+
+	    /* Compute firstpos. */
+	    if (cat->left->nullable)
+	      {
+		/* The left side matches the empty string.  Make a first pass
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
+		status = tre_match_empty(stack, cat->left,
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
+		if (status != REG_OK)
+		  return status;
+		/* Allocate arrays for the tags and parameters. */
+		tags = xmalloc(sizeof(*tags) * (num_tags + 1));
+		if (!tags)
+		  return REG_ESPACE;
+		tags[0] = -1;
+		assertions = 0;
+		params = NULL;
+		if (params_seen)
+		  {
+		    params = tre_mem_alloc(mem, sizeof(*params)
+					   * TRE_PARAM_LAST);
+		    if (!params)
+		      {
+			xfree(tags);
+			return REG_ESPACE;
+		      }
+		  }
+		/* Second pass with tre_mach_empty() to get the list of
+		   tags and parameters. */
+		status = tre_match_empty(stack, cat->left, tags,
+					 &assertions, params, NULL, NULL);
+		if (status != REG_OK)
+		  {
+		    xfree(tags);
+		    return status;
+		  }
+		node->firstpos =
+		  tre_set_union(mem, cat->right->firstpos, cat->left->firstpos,
+				tags, assertions, params);
+		xfree(tags);
+		if (!node->firstpos)
+		  return REG_ESPACE;
+	      }
+	    else
+	      {
+		node->firstpos = cat->left->firstpos;
+	      }
+
+	    /* Compute lastpos. */
+	    if (cat->right->nullable)
+	      {
+		/* The right side matches the empty string.  Make a first pass
+		   with tre_match_empty() to get the number of tags and
+		   parameters. */
+		status = tre_match_empty(stack, cat->right,
+					 NULL, NULL, NULL, &num_tags,
+					 &params_seen);
+		if (status != REG_OK)
+		  return status;
+		/* Allocate arrays for the tags and parameters. */
+		tags = xmalloc(sizeof(int) * (num_tags + 1));
+		if (!tags)
+		  return REG_ESPACE;
+		tags[0] = -1;
+		assertions = 0;
+		params = NULL;
+		if (params_seen)
+		  {
+		    params = tre_mem_alloc(mem, sizeof(*params)
+					   * TRE_PARAM_LAST);
+		    if (!params)
+		      {
+			xfree(tags);
+			return REG_ESPACE;
+		      }
+		  }
+		/* Second pass with tre_mach_empty() to get the list of
+		   tags and parameters. */
+		status = tre_match_empty(stack, cat->right, tags,
+					 &assertions, params, NULL, NULL);
+		if (status != REG_OK)
+		  {
+		    xfree(tags);
+		    return status;
+		  }
+		node->lastpos =
+		  tre_set_union(mem, cat->left->lastpos, cat->right->lastpos,
+				tags, assertions, params);
+		xfree(tags);
+		if (!node->lastpos)
+		  return REG_ESPACE;
+	      }
+	    else
+	      {
+		node->lastpos = cat->right->lastpos;
+	      }
+	    break;
+	  }
+
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  return REG_OK;
+}
+
+
+/* Adds a transition from each position in `p1' to each position in `p2'. */
+static reg_errcode_t
+tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
+	       tre_tnfa_transition_t *transitions,
+	       int *counts, int *offs)
+{
+  tre_pos_and_tags_t *orig_p2 = p2;
+  tre_tnfa_transition_t *trans;
+  int i, j, k, l, dup, prev_p2_pos;
+
+  if (transitions != NULL)
+    while (p1->position >= 0)
+      {
+	p2 = orig_p2;
+	prev_p2_pos = -1;
+	while (p2->position >= 0)
+	  {
+	    /* Optimization: if this position was already handled, skip it. */
+	    if (p2->position == prev_p2_pos)
+	      {
+		p2++;
+		continue;
+	      }
+	    prev_p2_pos = p2->position;
+	    /* Set `trans' to point to the next unused transition from
+	       position `p1->position'. */
+	    trans = transitions + offs[p1->position];
+	    while (trans->state != NULL)
+	      {
+#if 0
+		/* If we find a previous transition from `p1->position' to
+		   `p2->position', it is overwritten.  This can happen only
+		   if there are nested loops in the regexp, like in "((a)*)*".
+		   In POSIX.2 repetition using the outer loop is always
+		   preferred over using the inner loop.	 Therefore the
+		   transition for the inner loop is useless and can be thrown
+		   away. */
+		/* XXX - The same position is used for all nodes in a bracket
+		   expression, so this optimization cannot be used (it will
+		   break bracket expressions) unless I figure out a way to
+		   detect it here. */
+		if (trans->state_id == p2->position)
+		  {
+		    DPRINT(("*"));
+		    break;
+		  }
+#endif
+		trans++;
+	      }
+
+	    if (trans->state == NULL)
+	      (trans + 1)->state = NULL;
+	    /* Use the character ranges, assertions, etc. from `p1' for
+	       the transition from `p1' to `p2'. */
+	    trans->code_min = p1->code_min;
+	    trans->code_max = p1->code_max;
+	    trans->state = transitions + offs[p2->position];
+	    trans->state_id = p2->position;
+	    trans->assertions = p1->assertions | p2->assertions
+	      | (p1->class ? ASSERT_CHAR_CLASS : 0)
+	      | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0);
+	    if (p1->backref >= 0)
+	      {
+		assert((trans->assertions & ASSERT_CHAR_CLASS) == 0);
+		assert(p2->backref < 0);
+		trans->u.backref = p1->backref;
+		trans->assertions |= ASSERT_BACKREF;
+	      }
+	    else
+	      trans->u.class = p1->class;
+	    if (p1->neg_classes != NULL)
+	      {
+		for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++);
+		trans->neg_classes =
+		  xmalloc(sizeof(*trans->neg_classes) * (i + 1));
+		if (trans->neg_classes == NULL)
+		  return REG_ESPACE;
+		for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++)
+		  trans->neg_classes[i] = p1->neg_classes[i];
+		trans->neg_classes[i] = (tre_ctype_t)0;
+	      }
+	    else
+	      trans->neg_classes = NULL;
+
+	    /* Find out how many tags this transition has. */
+	    i = 0;
+	    if (p1->tags != NULL)
+	      while(p1->tags[i] >= 0)
+		i++;
+	    j = 0;
+	    if (p2->tags != NULL)
+	      while(p2->tags[j] >= 0)
+		j++;
+
+	    /* If we are overwriting a transition, free the old tag array. */
+	    if (trans->tags != NULL)
+	      xfree(trans->tags);
+	    trans->tags = NULL;
+
+	    /* If there were any tags, allocate an array and fill it. */
+	    if (i + j > 0)
+	      {
+		trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1));
+		if (!trans->tags)
+		  return REG_ESPACE;
+		i = 0;
+		if (p1->tags != NULL)
+		  while(p1->tags[i] >= 0)
+		    {
+		      trans->tags[i] = p1->tags[i];
+		      i++;
+		    }
+		l = i;
+		j = 0;
+		if (p2->tags != NULL)
+		  while (p2->tags[j] >= 0)
+		    {
+		      /* Don't add duplicates. */
+		      dup = 0;
+		      for (k = 0; k < i; k++)
+			if (trans->tags[k] == p2->tags[j])
+			  {
+			    dup = 1;
+			    break;
+			  }
+		      if (!dup)
+			trans->tags[l++] = p2->tags[j];
+		      j++;
+		    }
+		trans->tags[l] = -1;
+	      }
+
+	    /* Set the parameter array.	 If both `p2' and `p1' have same
+	       parameters, the values in `p2' override those in `p1'. */
+	    if (p1->params || p2->params)
+	      {
+		if (!trans->params)
+		  trans->params = xmalloc(sizeof(*trans->params)
+					  * TRE_PARAM_LAST);
+		if (!trans->params)
+		  return REG_ESPACE;
+		for (i = 0; i < TRE_PARAM_LAST; i++)
+		  {
+		    trans->params[i] = TRE_PARAM_UNSET;
+		    if (p1->params && p1->params[i] != TRE_PARAM_UNSET)
+		      trans->params[i] = p1->params[i];
+		    if (p2->params && p2->params[i] != TRE_PARAM_UNSET)
+		      trans->params[i] = p2->params[i];
+		  }
+	      }
+	    else
+	      {
+		if (trans->params)
+		  xfree(trans->params);
+		trans->params = NULL;
+	      }
+
+
+#ifdef TRE_DEBUG
+	    {
+	      int *tags;
+
+	      DPRINT(("	 %2d -> %2d on %3d", p1->position, p2->position,
+		      p1->code_min));
+	      if (p1->code_max != p1->code_min)
+		DPRINT(("-%3d", p1->code_max));
+	      tags = trans->tags;
+	      if (tags)
+		{
+		  DPRINT((", tags ["));
+		  while (*tags >= 0)
+		    {
+		      DPRINT(("%d", *tags));
+		      tags++;
+		      if (*tags >= 0)
+			DPRINT((","));
+		    }
+		  DPRINT(("]"));
+		}
+	      if (trans->assertions)
+		DPRINT((", assert %d", trans->assertions));
+	      if (trans->assertions & ASSERT_BACKREF)
+		DPRINT((", backref %d", trans->u.backref));
+	      else if (trans->u.class)
+		DPRINT((", class %ld", (long)trans->u.class));
+	      if (trans->neg_classes)
+		DPRINT((", neg_classes %p", trans->neg_classes));
+	      if (trans->params)
+		{
+		  DPRINT((", "));
+		  tre_print_params(trans->params);
+		}
+	      DPRINT(("\n"));
+	    }
+#endif /* TRE_DEBUG */
+	    p2++;
+	  }
+	p1++;
+      }
+  else
+    /* Compute a maximum limit for the number of transitions leaving
+       from each state. */
+    while (p1->position >= 0)
+      {
+	p2 = orig_p2;
+	while (p2->position >= 0)
+	  {
+	    counts[p1->position]++;
+	    p2++;
+	  }
+	p1++;
+      }
+  return REG_OK;
+}
+
+/* Converts the syntax tree to a TNFA.	All the transitions in the TNFA are
+   labelled with one character range (there are no transitions on empty
+   strings).  The TNFA takes O(n^2) space in the worst case, `n' is size of
+   the regexp. */
+static reg_errcode_t
+tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions,
+		int *counts, int *offs)
+{
+  tre_union_t *uni;
+  tre_catenation_t *cat;
+  tre_iteration_t *iter;
+  reg_errcode_t errcode = REG_OK;
+
+  /* XXX - recurse using a stack!. */
+  switch (node->type)
+    {
+    case LITERAL:
+      break;
+    case UNION:
+      uni = (tre_union_t *)node->obj;
+      errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs);
+      break;
+
+    case CATENATION:
+      cat = (tre_catenation_t *)node->obj;
+      /* Add a transition from each position in cat->left->lastpos
+	 to each position in cat->right->firstpos. */
+      errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos,
+			       transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs);
+      if (errcode != REG_OK)
+	return errcode;
+      errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs);
+      break;
+
+    case ITERATION:
+      iter = (tre_iteration_t *)node->obj;
+      assert(iter->max == -1 || iter->max == 1);
+
+      if (iter->max == -1)
+	{
+	  assert(iter->min == 0 || iter->min == 1);
+	  /* Add a transition from each last position in the iterated
+	     expression to each first position. */
+	  errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos,
+				   transitions, counts, offs);
+	  if (errcode != REG_OK)
+	    return errcode;
+	}
+      errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs);
+      break;
+    }
+  return errcode;
+}
+
+
+#define ERROR_EXIT(err)		  \
+  do				  \
+    {				  \
+      errcode = err;		  \
+      if (/*CONSTCOND*/1)	  \
+      	goto error_exit;	  \
+    }				  \
+ while (/*CONSTCOND*/0)
+
+
+int
+tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags)
+{
+  tre_stack_t *stack;
+  tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r;
+  tre_pos_and_tags_t *p;
+  int *counts = NULL, *offs = NULL;
+  int i, add = 0;
+  tre_tnfa_transition_t *transitions, *initial;
+  tre_tnfa_t *tnfa = NULL;
+  tre_submatch_data_t *submatch_data;
+  tre_tag_direction_t *tag_directions = NULL;
+  reg_errcode_t errcode;
+  tre_mem_t mem;
+
+  /* Parse context. */
+  tre_parse_ctx_t parse_ctx;
+
+  /* Allocate a stack used throughout the compilation process for various
+     purposes. */
+  stack = tre_stack_new(512, 10240, 128);
+  if (!stack)
+    return REG_ESPACE;
+  /* Allocate a fast memory allocator. */
+  mem = tre_mem_new();
+  if (!mem)
+    {
+      tre_stack_destroy(stack);
+      return REG_ESPACE;
+    }
+
+  /* Parse the regexp. */
+  memset(&parse_ctx, 0, sizeof(parse_ctx));
+  parse_ctx.mem = mem;
+  parse_ctx.stack = stack;
+  parse_ctx.re = regex;
+  parse_ctx.len = n;
+  parse_ctx.cflags = cflags;
+  parse_ctx.max_backref = -1;
+  DPRINT(("tre_compile: parsing '%.*" STRF "'\n", (int)n, regex));
+  errcode = tre_parse(&parse_ctx);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+  preg->re_nsub = parse_ctx.submatch_id - 1;
+  tree = parse_ctx.result;
+
+  /* Back references and approximate matching cannot currently be used
+     in the same regexp. */
+  if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx)
+    ERROR_EXIT(REG_BADPAT);
+
+#ifdef TRE_DEBUG
+  tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+  /* Referring to nonexistent subexpressions is illegal. */
+  if (parse_ctx.max_backref > (int)preg->re_nsub)
+    ERROR_EXIT(REG_ESUBREG);
+
+  /* Allocate the TNFA struct. */
+  tnfa = xcalloc(1, sizeof(tre_tnfa_t));
+  if (tnfa == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+  tnfa->have_approx = parse_ctx.have_approx;
+  tnfa->num_submatches = parse_ctx.submatch_id;
+
+  /* Set up tags for submatch addressing.  If REG_NOSUB is set and the
+     regexp does not have back references, this can be skipped. */
+  if (tnfa->have_backrefs || !(cflags & REG_NOSUB))
+    {
+      DPRINT(("tre_compile: setting up tags\n"));
+
+      /* Figure out how many tags we will need. */
+      errcode = tre_add_tags(NULL, stack, tree, tnfa);
+      if (errcode != REG_OK)
+	ERROR_EXIT(errcode);
+#ifdef TRE_DEBUG
+      tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+      if (tnfa->num_tags > 0)
+	{
+	  tag_directions = xmalloc(sizeof(*tag_directions)
+				   * (tnfa->num_tags + 1));
+	  if (tag_directions == NULL)
+	    ERROR_EXIT(REG_ESPACE);
+	  tnfa->tag_directions = tag_directions;
+	  memset(tag_directions, -1,
+		 sizeof(*tag_directions) * (tnfa->num_tags + 1));
+	}
+      tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1,
+				   sizeof(tnfa->minimal_tags));
+      if (tnfa->minimal_tags == NULL)
+	ERROR_EXIT(REG_ESPACE);
+
+      submatch_data = xcalloc((unsigned)parse_ctx.submatch_id,
+			      sizeof(*submatch_data));
+      if (submatch_data == NULL)
+	ERROR_EXIT(REG_ESPACE);
+      tnfa->submatch_data = submatch_data;
+
+      errcode = tre_add_tags(mem, stack, tree, tnfa);
+      if (errcode != REG_OK)
+	ERROR_EXIT(errcode);
+
+#ifdef TRE_DEBUG
+      for (i = 0; i < parse_ctx.submatch_id; i++)
+	DPRINT(("pmatch[%d] = {t%d, t%d}\n",
+		i, submatch_data[i].so_tag, submatch_data[i].eo_tag));
+      for (i = 0; i < tnfa->num_tags; i++)
+	DPRINT(("t%d is %s\n", i,
+		tag_directions[i] == TRE_TAG_MINIMIZE ?
+		"minimized" : "maximized"));
+#endif /* TRE_DEBUG */
+    }
+
+  /* Expand iteration nodes. */
+  errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position,
+			   tag_directions, &tnfa->params_depth);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  /* Add a dummy node for the final state.
+     XXX - For certain patterns this dummy node can be optimized away,
+	   for example "a*" or "ab*".	Figure out a simple way to detect
+	   this possibility. */
+  tmp_ast_l = tree;
+  tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++);
+  if (tmp_ast_r == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r);
+  if (tree == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+#ifdef TRE_DEBUG
+  tre_ast_print(tree);
+  DPRINT(("Number of states: %d\n", parse_ctx.position));
+#endif /* TRE_DEBUG */
+
+  errcode = tre_compute_nfl(mem, stack, tree);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  counts = xmalloc(sizeof(int) * parse_ctx.position);
+  if (counts == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  offs = xmalloc(sizeof(int) * parse_ctx.position);
+  if (offs == NULL)
+    ERROR_EXIT(REG_ESPACE);
+
+  for (i = 0; i < parse_ctx.position; i++)
+    counts[i] = 0;
+  tre_ast_to_tnfa(tree, NULL, counts, NULL);
+
+  add = 0;
+  for (i = 0; i < parse_ctx.position; i++)
+    {
+      offs[i] = add;
+      add += counts[i] + 1;
+      counts[i] = 0;
+    }
+  transitions = xcalloc((unsigned)add + 1, sizeof(*transitions));
+  if (transitions == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->transitions = transitions;
+  tnfa->num_transitions = add;
+
+  DPRINT(("Converting to TNFA:\n"));
+  errcode = tre_ast_to_tnfa(tree, transitions, counts, offs);
+  if (errcode != REG_OK)
+    ERROR_EXIT(errcode);
+
+  /* If in eight bit mode, compute a table of characters that can be the
+     first character of a match. */
+  tnfa->first_char = -1;
+  if (TRE_MB_CUR_MAX == 1 && !tmp_ast_l->nullable)
+    {
+      int count = 0;
+      tre_cint_t k;
+      DPRINT(("Characters that can start a match:"));
+      tnfa->firstpos_chars = xcalloc(256, sizeof(char));
+      if (tnfa->firstpos_chars == NULL)
+	ERROR_EXIT(REG_ESPACE);
+      for (p = tree->firstpos; p->position >= 0; p++)
+	{
+	  tre_tnfa_transition_t *j = transitions + offs[p->position];
+	  while (j->state != NULL)
+	    {
+	      for (k = j->code_min; k <= j->code_max && k < 256; k++)
+		{
+		  DPRINT((" %d", k));
+		  tnfa->firstpos_chars[k] = 1;
+		  count++;
+		}
+	      j++;
+	    }
+	}
+      DPRINT(("\n"));
+#define TRE_OPTIMIZE_FIRST_CHAR 1
+#if TRE_OPTIMIZE_FIRST_CHAR
+      if (count == 1)
+	{
+	  for (k = 0; k < 256; k++)
+	    if (tnfa->firstpos_chars[k])
+	      {
+		DPRINT(("first char must be %d\n", k));
+		tnfa->first_char = k;
+		xfree(tnfa->firstpos_chars);
+		tnfa->firstpos_chars = NULL;
+		break;
+	      }
+	}
+#endif
+
+    }
+  else
+    tnfa->firstpos_chars = NULL;
+
+
+  p = tree->firstpos;
+  i = 0;
+  while (p->position >= 0)
+    {
+      i++;
+
+#ifdef TRE_DEBUG
+      {
+	int *tags;
+	DPRINT(("initial: %d", p->position));
+	tags = p->tags;
+	if (tags != NULL)
+	  {
+	    if (*tags >= 0)
+	      DPRINT(("/"));
+	    while (*tags >= 0)
+	      {
+		DPRINT(("%d", *tags));
+		tags++;
+		if (*tags >= 0)
+		  DPRINT((","));
+	      }
+	  }
+	DPRINT((", assert %d", p->assertions));
+	if (p->params)
+	  {
+	    DPRINT((", "));
+	    tre_print_params(p->params);
+	  }
+	DPRINT(("\n"));
+      }
+#endif /* TRE_DEBUG */
+
+      p++;
+    }
+
+  initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t));
+  if (initial == NULL)
+    ERROR_EXIT(REG_ESPACE);
+  tnfa->initial = initial;
+
+  i = 0;
+  for (p = tree->firstpos; p->position >= 0; p++)
+    {
+      initial[i].state = transitions + offs[p->position];
+      initial[i].state_id = p->position;
+      initial[i].tags = NULL;
+      /* Copy the arrays p->tags, and p->params, they are allocated
+	 from a tre_mem object. */
+      if (p->tags)
+	{
+	  int j;
+	  for (j = 0; p->tags[j] >= 0; j++);
+	  initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1));
+	  if (!initial[i].tags)
+	    ERROR_EXIT(REG_ESPACE);
+	  memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1));
+	}
+      initial[i].params = NULL;
+      if (p->params)
+	{
+	  initial[i].params = xmalloc(sizeof(*p->params) * TRE_PARAM_LAST);
+	  if (!initial[i].params)
+	    ERROR_EXIT(REG_ESPACE);
+	  memcpy(initial[i].params, p->params,
+		 sizeof(*p->params) * TRE_PARAM_LAST);
+	}
+      initial[i].assertions = p->assertions;
+      i++;
+    }
+  initial[i].state = NULL;
+
+  tnfa->num_transitions = add;
+  tnfa->final = transitions + offs[tree->lastpos[0].position];
+  tnfa->num_states = parse_ctx.position;
+  tnfa->cflags = cflags;
+
+  DPRINT(("final state %p\n", (void *)tnfa->final));
+
+  tre_mem_destroy(mem);
+  tre_stack_destroy(stack);
+  xfree(counts);
+  xfree(offs);
+
+  preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+  return REG_OK;
+
+ error_exit:
+  /* Free everything that was allocated and return the error code. */
+  tre_mem_destroy(mem);
+  if (stack != NULL)
+    tre_stack_destroy(stack);
+  if (counts != NULL)
+    xfree(counts);
+  if (offs != NULL)
+    xfree(offs);
+  preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+  tre_free(preg);
+  return errcode;
+}
+
+
+
+
+void
+tre_free(regex_t *preg)
+{
+  tre_tnfa_t *tnfa;
+  unsigned int i;
+  tre_tnfa_transition_t *trans;
+
+  tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+  if (!tnfa)
+    return;
+
+  for (i = 0; i < tnfa->num_transitions; i++)
+    if (tnfa->transitions[i].state)
+      {
+	if (tnfa->transitions[i].tags)
+	  xfree(tnfa->transitions[i].tags);
+	if (tnfa->transitions[i].neg_classes)
+	  xfree(tnfa->transitions[i].neg_classes);
+	if (tnfa->transitions[i].params)
+	  xfree(tnfa->transitions[i].params);
+      }
+  if (tnfa->transitions)
+    xfree(tnfa->transitions);
+
+  if (tnfa->initial)
+    {
+      for (trans = tnfa->initial; trans->state; trans++)
+	{
+	  if (trans->tags)
+	    xfree(trans->tags);
+	  if (trans->params)
+	    xfree(trans->params);
+	}
+      xfree(tnfa->initial);
+    }
+
+  if (tnfa->submatch_data)
+    {
+      for (i = 0; i < tnfa->num_submatches; i++)
+	if (tnfa->submatch_data[i].parents)
+	  xfree(tnfa->submatch_data[i].parents);
+      xfree(tnfa->submatch_data);
+    }
+
+  if (tnfa->tag_directions)
+    xfree(tnfa->tag_directions);
+  if (tnfa->firstpos_chars)
+    xfree(tnfa->firstpos_chars);
+  if (tnfa->minimal_tags)
+    xfree(tnfa->minimal_tags);
+  xfree(tnfa);
+}
+
+char *
+tre_version(void)
+{
+  static char str[256];
+  char *version;
+
+  if (str[0] == 0)
+    {
+      (void) tre_config(TRE_CONFIG_VERSION, &version);
+      (void) snprintf(str, sizeof(str), "TRE %s (BSD)", version);
+    }
+  return str;
+}
+
+int
+tre_config(int query, void *result)
+{
+  int *int_result = result;
+  const char **string_result = result;
+
+  switch (query)
+    {
+    case TRE_CONFIG_APPROX:
+#ifdef TRE_APPROX
+      *int_result = 1;
+#else /* !TRE_APPROX */
+      *int_result = 0;
+#endif /* !TRE_APPROX */
+      return REG_OK;
+
+    case TRE_CONFIG_WCHAR:
+#ifdef TRE_WCHAR
+      *int_result = 1;
+#else /* !TRE_WCHAR */
+      *int_result = 0;
+#endif /* !TRE_WCHAR */
+      return REG_OK;
+
+    case TRE_CONFIG_MULTIBYTE:
+#ifdef TRE_MULTIBYTE
+      *int_result = 1;
+#else /* !TRE_MULTIBYTE */
+      *int_result = 0;
+#endif /* !TRE_MULTIBYTE */
+      return REG_OK;
+
+    case TRE_CONFIG_SYSTEM_ABI:
+#ifdef TRE_CONFIG_SYSTEM_ABI
+      *int_result = 1;
+#else /* !TRE_CONFIG_SYSTEM_ABI */
+      *int_result = 0;
+#endif /* !TRE_CONFIG_SYSTEM_ABI */
+      return REG_OK;
+
+    case TRE_CONFIG_VERSION:
+      *string_result = TRE_VERSION;
+      return REG_OK;
+    }
+
+  return REG_NOMATCH;
+}
+
+
+/* EOF */
+/*
+  tre-match-approx.c - TRE approximate regex matching engine
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+/* AIX requires this to be the first thing in the file.	 */
+#ifdef TRE_USE_ALLOCA
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+#endif /* TRE_USE_ALLOCA */
+
+#define __USE_STRING_INLINES
+#undef __NO_INLINE__
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* HAVE_WCTYPE_H */
+#ifndef TRE_WCHAR
+#include <ctype.h>
+#endif /* !TRE_WCHAR */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+
+/*
+  tre-match-utils.h - TRE matcher helper definitions
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#define str_source ((const tre_str_source*)string)
+
+#ifdef TRE_WCHAR
+
+#ifdef TRE_MULTIBYTE
+
+/* Wide character and multibyte support. */
+
+#define GET_NEXT_WCHAR()						      \
+  do {									      \
+    prev_c = next_c;							      \
+    if (type == STR_BYTE)						      \
+      {									      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = '\0';						      \
+	else								      \
+	  next_c = (unsigned char)(*str_byte++);			      \
+      }									      \
+    else if (type == STR_WIDE)						      \
+      {									      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = L'\0';						      \
+	else								      \
+	  next_c = *str_wide++;						      \
+      }									      \
+    else if (type == STR_MBS)						      \
+      {									      \
+        pos += pos_add_next;					      	      \
+	if (str_byte == NULL)						      \
+	  next_c = L'\0';						      \
+	else								      \
+	  {								      \
+	    size_t w;							      \
+	    int max;							      \
+	    if (len >= 0)						      \
+	      max = len - pos;						      \
+	    else							      \
+	      max = 32;							      \
+	    if (max <= 0)						      \
+	      {								      \
+		next_c = L'\0';						      \
+		pos_add_next = 1;					      \
+	      }								      \
+	    else							      \
+	      {								      \
+		w = tre_mbrtowc(&next_c, str_byte, (size_t)max, &mbstate);    \
+		if (w == (size_t)-1 || w == (size_t)-2)			      \
+		  return REG_NOMATCH;					      \
+		if (w == 0 && len >= 0)					      \
+		  {							      \
+		    pos_add_next = 1;					      \
+		    next_c = 0;						      \
+		    str_byte++;						      \
+		  }							      \
+		else							      \
+		  {							      \
+		    pos_add_next = w;					      \
+		    str_byte += w;					      \
+		  }							      \
+	      }								      \
+	  }								      \
+      }									      \
+    else if (type == STR_USER)						      \
+      {									      \
+        pos += pos_add_next;					      	      \
+	str_user_end = str_source->get_next_char(&next_c, &pos_add_next,      \
+                                                 str_source->context);	      \
+      }									      \
+  } while(/*CONSTCOND*/0)
+
+#else /* !TRE_MULTIBYTE */
+
+/* Wide character support, no multibyte support. */
+
+#define GET_NEXT_WCHAR()						      \
+  do {									      \
+    prev_c = next_c;							      \
+    if (type == STR_BYTE)						      \
+      {									      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = '\0';						      \
+	else								      \
+	  next_c = (unsigned char)(*str_byte++);			      \
+      }									      \
+    else if (type == STR_WIDE)						      \
+      {									      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = L'\0';						      \
+	else								      \
+	  next_c = *str_wide++;						      \
+      }									      \
+    else if (type == STR_USER)						      \
+      {									      \
+        pos += pos_add_next;					      	      \
+	str_user_end = str_source->get_next_char(&next_c, &pos_add_next,      \
+                                                 str_source->context);	      \
+      }									      \
+  } while(/*CONSTCOND*/0)
+
+#endif /* !TRE_MULTIBYTE */
+
+#else /* !TRE_WCHAR */
+
+/* No wide character or multibyte support. */
+
+#define GET_NEXT_WCHAR()						      \
+  do {									      \
+    prev_c = next_c;							      \
+    if (type == STR_BYTE)						      \
+      {									      \
+	pos++;								      \
+	if (len >= 0 && pos >= len)					      \
+	  next_c = '\0';						      \
+	else								      \
+	  next_c = (unsigned char)(*str_byte++);			      \
+      }									      \
+    else if (type == STR_USER)						      \
+      {									      \
+	pos += pos_add_next;						      \
+	str_user_end = str_source->get_next_char(&next_c, &pos_add_next,      \
+						 str_source->context);	      \
+      }									      \
+  } while(/*CONSTCOND*/0)
+
+#endif /* !TRE_WCHAR */
+
+
+
+#define IS_WORD_CHAR(c)	 ((c) == L'_' || tre_isalnum(c))
+
+#define CHECK_ASSERTIONS(assertions)					      \
+  (((assertions & ASSERT_AT_BOL)					      \
+    && (pos > 0 || reg_notbol)						      \
+    && (prev_c != L'\n' || !reg_newline))				      \
+   || ((assertions & ASSERT_AT_EOL)					      \
+       && (next_c != L'\0' || reg_noteol)				      \
+       && (next_c != L'\n' || !reg_newline))				      \
+   || ((assertions & ASSERT_AT_BOW)					      \
+       && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c)))	              \
+   || ((assertions & ASSERT_AT_EOW)					      \
+       && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c)))		      \
+   || ((assertions & ASSERT_AT_WB)					      \
+       && (pos != 0 && next_c != L'\0'					      \
+	   && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c)))		      \
+   || ((assertions & ASSERT_AT_WB_NEG)					      \
+       && (pos == 0 || next_c == L'\0'					      \
+	   || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
+
+#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)                             \
+  (((trans_i->assertions & ASSERT_CHAR_CLASS)                                 \
+       && !(tnfa->cflags & REG_ICASE)                                         \
+       && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class))                 \
+    || ((trans_i->assertions & ASSERT_CHAR_CLASS)                             \
+        && (tnfa->cflags & REG_ICASE)                                         \
+        && !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class)     \
+	&& !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class))    \
+    || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG)                         \
+        && tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c,\
+                                      tnfa->cflags & REG_ICASE)))
+
+
+
+
+/* Returns 1 if `t1' wins `t2', 0 otherwise. */
+inline static int
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+	      int *t1, int *t2)
+{
+  int i;
+  for (i = 0; i < num_tags; i++)
+    {
+      if (tag_directions[i] == TRE_TAG_MINIMIZE)
+	{
+	  if (t1[i] < t2[i])
+	    return 1;
+	  if (t1[i] > t2[i])
+	    return 0;
+	}
+      else
+	{
+	  if (t1[i] > t2[i])
+	    return 1;
+	  if (t1[i] < t2[i])
+	    return 0;
+	}
+    }
+  /*  assert(0);*/
+  return 0;
+}
+
+inline static int
+tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase)
+{
+  DPRINT(("neg_char_classes_test: %p, %d, %d\n", classes, wc, icase));
+  while (*classes != (tre_ctype_t)0)
+    if ((!icase && tre_isctype(wc, *classes))
+	|| (icase && (tre_isctype(tre_toupper(wc), *classes)
+		      || tre_isctype(tre_tolower(wc), *classes))))
+      return 1; /* Match. */
+    else
+      classes++;
+  return 0; /* No match. */
+}
+
+#define TRE_M_COST	0
+#define TRE_M_NUM_INS	1
+#define TRE_M_NUM_DEL	2
+#define TRE_M_NUM_SUBST 3
+#define TRE_M_NUM_ERR	4
+#define TRE_M_LAST	5
+
+#define TRE_M_MAX_DEPTH 3
+
+typedef struct {
+  /* State in the TNFA transition table. */
+  tre_tnfa_transition_t *state;
+  /* Position in input string. */
+  int pos;
+  /* Tag values. */
+  int *tags;
+  /* Matching parameters. */
+  regaparams_t params;
+  /* Nesting depth of parameters.  This is used as an index in
+     the `costs' array. */
+  int depth;
+  /* Costs and counter values for different parameter nesting depths. */
+  int costs[TRE_M_MAX_DEPTH + 1][TRE_M_LAST];
+} tre_tnfa_approx_reach_t;
+
+
+#ifdef TRE_DEBUG
+/* Prints the `reach' array in a readable fashion with DPRINT. */
+static void
+tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_approx_reach_t *reach,
+		int pos, int num_tags)
+{
+  int id;
+
+  /* Print each state on one line. */
+  DPRINT(("  reach:\n"));
+  for (id = 0; id < tnfa->num_states; id++)
+    {
+      int i, j;
+      if (reach[id].pos < pos)
+	continue;  /* Not reached. */
+      DPRINT(("	 %03d, costs ", id));
+      for (i = 0; i <= reach[id].depth; i++)
+	{
+	  DPRINT(("["));
+	  for (j = 0; j < TRE_M_LAST; j++)
+	    {
+	      DPRINT(("%2d", reach[id].costs[i][j]));
+	      if (j + 1 < TRE_M_LAST)
+		DPRINT((","));
+	    }
+	  DPRINT(("]"));
+	  if (i + 1 <= reach[id].depth)
+	    DPRINT((", "));
+	}
+      DPRINT(("\n	tags "));
+      for (i = 0; i < num_tags; i++)
+	{
+	  DPRINT(("%02d", reach[id].tags[i]));
+	  if (i + 1 < num_tags)
+	    DPRINT((","));
+	}
+      DPRINT(("\n"));
+    }
+  DPRINT(("\n"));
+}
+#endif /* TRE_DEBUG */
+
+
+/* Sets the matching parameters in `reach' to the ones defined in the `pa'
+   array.  If `pa' specifies default values, they are taken from
+   `default_params'. */
+inline static void
+tre_set_params(tre_tnfa_approx_reach_t *reach,
+	       int *pa, regaparams_t default_params)
+{
+  int value;
+
+  /* If depth is increased reset costs and counters to zero for the
+     new levels. */
+  value = pa[TRE_PARAM_DEPTH];
+  assert(value <= TRE_M_MAX_DEPTH);
+  if (value > reach->depth)
+    {
+      int i, j;
+      for (i = reach->depth + 1; i <= value; i++)
+	for (j = 0; j < TRE_M_LAST; j++)
+	  reach->costs[i][j] = 0;
+    }
+  reach->depth = value;
+
+  /* Set insert cost. */
+  value = pa[TRE_PARAM_COST_INS];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.cost_ins = default_params.cost_ins;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.cost_ins = value;
+
+  /* Set delete cost. */
+  value = pa[TRE_PARAM_COST_DEL];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.cost_del = default_params.cost_del;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.cost_del = value;
+
+  /* Set substitute cost. */
+  value = pa[TRE_PARAM_COST_SUBST];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.cost_subst = default_params.cost_subst;
+  else
+    reach->params.cost_subst = value;
+
+  /* Set maximum cost. */
+  value = pa[TRE_PARAM_COST_MAX];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.max_cost = default_params.max_cost;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.max_cost = value;
+
+  /* Set maximum inserts. */
+  value = pa[TRE_PARAM_MAX_INS];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.max_ins = default_params.max_ins;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.max_ins = value;
+
+  /* Set maximum deletes. */
+  value = pa[TRE_PARAM_MAX_DEL];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.max_del = default_params.max_del;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.max_del = value;
+
+  /* Set maximum substitutes. */
+  value = pa[TRE_PARAM_MAX_SUBST];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.max_subst = default_params.max_subst;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.max_subst = value;
+
+  /* Set maximum number of errors. */
+  value = pa[TRE_PARAM_MAX_ERR];
+  if (value == TRE_PARAM_DEFAULT)
+    reach->params.max_err = default_params.max_err;
+  else if (value != TRE_PARAM_UNSET)
+    reach->params.max_err = value;
+}
+
+reg_errcode_t
+tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, int len,
+		    tre_str_type_t type, int *match_tags,
+		    regamatch_t *match, regaparams_t default_params,
+		    int eflags, int *match_end_ofs)
+{
+  /* State variables required by GET_NEXT_WCHAR. */
+  tre_char_t prev_c = 0, next_c = 0;
+  const char *str_byte = string;
+  int pos = -1;
+  unsigned int pos_add_next = 1;
+#ifdef TRE_WCHAR
+  const wchar_t *str_wide = string;
+#ifdef TRE_MBSTATE
+  mbstate_t mbstate;
+#endif /* !TRE_WCHAR */
+#endif /* TRE_WCHAR */
+  int reg_notbol = eflags & REG_NOTBOL;
+  int reg_noteol = eflags & REG_NOTEOL;
+  int reg_newline = tnfa->cflags & REG_NEWLINE;
+  int str_user_end = 0;
+
+  int prev_pos;
+
+  /* Number of tags. */
+  int num_tags;
+  /* The reach tables. */
+  tre_tnfa_approx_reach_t *reach, *reach_next;
+  /* Tag array for temporary use. */
+  int *tmp_tags;
+
+  /* End offset of best match so far, or -1 if no match found yet. */
+  int match_eo = -1;
+  /* Costs of the match. */
+  int match_costs[TRE_M_LAST];
+
+  /* Space for temporary data required for matching. */
+  unsigned char *buf;
+
+  int i, id;
+
+  if (!match_tags)
+    num_tags = 0;
+  else
+    num_tags = tnfa->num_tags;
+
+#ifdef TRE_MBSTATE
+  memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+  DPRINT(("tre_tnfa_run_approx, input type %d, len %d, eflags %d, "
+	  "match_tags %p\n",
+	  type, len, eflags,
+	  match_tags));
+  DPRINT(("max cost %d, ins %d, del %d, subst %d\n",
+	  default_params.max_cost,
+	  default_params.cost_ins,
+	  default_params.cost_del,
+	  default_params.cost_subst));
+
+  /* Allocate memory for temporary data required for matching.	This needs to
+     be done for every matching operation to be thread safe.  This allocates
+     everything in a single large block from the stack frame using alloca()
+     or with malloc() if alloca is unavailable. */
+  {
+    unsigned char *buf_cursor;
+    /* Space needed for one array of tags. */
+    int tag_bytes = sizeof(*tmp_tags) * num_tags;
+    /* Space needed for one reach table. */
+    int reach_bytes = sizeof(*reach_next) * tnfa->num_states;
+    /* Total space needed. */
+    int total_bytes = reach_bytes * 2 + (tnfa->num_states * 2 + 1 ) * tag_bytes;
+    /* Add some extra to make sure we can align the pointers.  The multiplier
+       used here must be equal to the number of ALIGN calls below. */
+    total_bytes += (sizeof(long) - 1) * 3;
+
+    /* Allocate the memory. */
+#ifdef TRE_USE_ALLOCA
+    buf = alloca(total_bytes);
+#else /* !TRE_USE_ALLOCA */
+    buf = xmalloc((unsigned)total_bytes);
+#endif /* !TRE_USE_ALLOCA */
+    if (!buf)
+      return REG_ESPACE;
+    memset(buf, 0, (size_t)total_bytes);
+
+    /* Allocate `tmp_tags' from `buf'. */
+    tmp_tags = (void *)buf;
+    buf_cursor = buf + tag_bytes;
+    buf_cursor += ALIGN(buf_cursor, long);
+
+    /* Allocate `reach' from `buf'. */
+    reach = (void *)buf_cursor;
+    buf_cursor += reach_bytes;
+    buf_cursor += ALIGN(buf_cursor, long);
+
+    /* Allocate `reach_next' from `buf'. */
+    reach_next = (void *)buf_cursor;
+    buf_cursor += reach_bytes;
+    buf_cursor += ALIGN(buf_cursor, long);
+
+    /* Allocate tag arrays for `reach' and `reach_next' from `buf'. */
+    for (i = 0; i < tnfa->num_states; i++)
+      {
+	reach[i].tags = (void *)buf_cursor;
+	buf_cursor += tag_bytes;
+	reach_next[i].tags = (void *)buf_cursor;
+	buf_cursor += tag_bytes;
+      }
+    assert(buf_cursor <= buf + total_bytes);
+  }
+
+  for (i = 0; i < TRE_M_LAST; i++)
+    match_costs[i] = INT_MAX;
+
+  /* Mark the reach arrays empty. */
+  for (i = 0; i < tnfa->num_states; i++)
+    reach[i].pos = reach_next[i].pos = -2;
+
+  prev_pos = pos;
+  GET_NEXT_WCHAR();
+  pos = 0;
+
+  while (/*CONSTCOND*/1)
+    {
+      DPRINT(("%03d:%2lc/%05d\n", pos, (tre_cint_t)next_c, (int)next_c));
+
+      /* Add initial states to `reach_next' if an exact match has not yet
+	 been found. */
+      if (match_costs[TRE_M_COST] > 0)
+	{
+	  tre_tnfa_transition_t *trans;
+	  DPRINT(("  init"));
+	  for (trans = tnfa->initial; trans->state; trans++)
+	    {
+	      int stateid = trans->state_id;
+
+	      /* If this state is not currently in `reach_next', add it
+		 there. */
+	      if (reach_next[stateid].pos < pos)
+		{
+		  if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
+		    {
+		      /* Assertions failed, don't add this state. */
+		      DPRINT((" !%d (assert)", stateid));
+		      continue;
+		    }
+		  DPRINT((" %d", stateid));
+		  reach_next[stateid].state = trans->state;
+		  reach_next[stateid].pos = pos;
+
+		  /* Compute tag values after this transition. */
+		  for (i = 0; i < num_tags; i++)
+		    reach_next[stateid].tags[i] = -1;
+
+		  if (trans->tags)
+		    for (i = 0; trans->tags[i] >= 0; i++)
+		      if (trans->tags[i] < num_tags)
+			reach_next[stateid].tags[trans->tags[i]] = pos;
+
+		  /* Set the parameters, depth, and costs. */
+		  reach_next[stateid].params = default_params;
+		  reach_next[stateid].depth = 0;
+		  for (i = 0; i < TRE_M_LAST; i++)
+		    reach_next[stateid].costs[0][i] = 0;
+		  if (trans->params)
+		    tre_set_params(&reach_next[stateid], trans->params,
+				   default_params);
+
+		  /* If this is the final state, mark the exact match. */
+		  if (trans->state == tnfa->final)
+		    {
+		      match_eo = pos;
+		      for (i = 0; i < num_tags; i++)
+			match_tags[i] = reach_next[stateid].tags[i];
+		      for (i = 0; i < TRE_M_LAST; i++)
+			match_costs[i] = 0;
+		    }
+		}
+	    }
+	    DPRINT(("\n"));
+	}
+
+
+      /* Handle inserts.  This is done by pretending there's an epsilon
+	 transition from each state in `reach' back to the same state.
+	 We don't need to worry about the final state here; this will never
+	 give a better match than what we already have. */
+      for (id = 0; id < tnfa->num_states; id++)
+	{
+	  int depth;
+	  int cost, cost0;
+
+	  if (reach[id].pos != prev_pos)
+	    {
+	      DPRINT(("	 insert: %d not reached\n", id));
+	      continue;	 /* Not reached. */
+	    }
+
+	  depth = reach[id].depth;
+
+	  /* Compute and check cost at current depth. */
+	  cost = reach[id].costs[depth][TRE_M_COST];
+	  if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
+	    cost += reach[id].params.cost_ins;
+	  if (cost > reach[id].params.max_cost)
+	    continue;  /* Cost too large. */
+
+	  /* Check number of inserts at current depth. */
+	  if (reach[id].costs[depth][TRE_M_NUM_INS] + 1
+	      > reach[id].params.max_ins)
+	    continue;  /* Too many inserts. */
+
+	  /* Check total number of errors at current depth. */
+	  if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
+	      > reach[id].params.max_err)
+	    continue;  /* Too many errors. */
+
+	  /* Compute overall cost. */
+	  cost0 = cost;
+	  if (depth > 0)
+	    {
+	      cost0 = reach[id].costs[0][TRE_M_COST];
+	      if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
+		cost0 += reach[id].params.cost_ins;
+	      else
+		cost0 += default_params.cost_ins;
+	    }
+
+	  DPRINT(("  insert: from %d to %d, cost %d: ", id, id,
+		  reach[id].costs[depth][TRE_M_COST]));
+	  if (reach_next[id].pos == pos
+	      && (cost0 >= reach_next[id].costs[0][TRE_M_COST]))
+	    {
+	      DPRINT(("lose\n"));
+	      continue;
+	    }
+	  DPRINT(("win\n"));
+
+	  /* Copy state, position, tags, parameters, and depth. */
+	  reach_next[id].state = reach[id].state;
+	  reach_next[id].pos = pos;
+	  for (i = 0; i < num_tags; i++)
+	    reach_next[id].tags[i] = reach[id].tags[i];
+	  reach_next[id].params = reach[id].params;
+	  reach_next[id].depth = reach[id].depth;
+
+	  /* Set the costs after this transition. */
+	  memcpy(reach_next[id].costs, reach[id].costs,
+		 sizeof(reach_next[id].costs[0][0])
+		 * TRE_M_LAST * (depth + 1));
+	  reach_next[id].costs[depth][TRE_M_COST] = cost;
+	  reach_next[id].costs[depth][TRE_M_NUM_INS]++;
+	  reach_next[id].costs[depth][TRE_M_NUM_ERR]++;
+	  if (depth > 0)
+	    {
+	      reach_next[id].costs[0][TRE_M_COST] = cost0;
+	      reach_next[id].costs[0][TRE_M_NUM_INS]++;
+	      reach_next[id].costs[0][TRE_M_NUM_ERR]++;
+	    }
+
+	}
+
+
+      /* Handle deletes.  This is done by traversing through the whole TNFA
+	 pretending that all transitions are epsilon transitions, until
+	 no more states can be reached with better costs. */
+      {
+	/* XXX - dynamic ringbuffer size */
+	tre_tnfa_approx_reach_t *ringbuffer[512];
+	tre_tnfa_approx_reach_t **deque_start, **deque_end;
+
+	deque_start = deque_end = ringbuffer;
+
+	/* Add all states in `reach_next' to the deque. */
+	for (id = 0; id < tnfa->num_states; id++)
+	  {
+	    if (reach_next[id].pos != pos)
+	      continue;
+	    *deque_end = &reach_next[id];
+	    deque_end++;
+	    assert(deque_end != deque_start);
+	  }
+
+	/* Repeat until the deque is empty. */
+	while (deque_end != deque_start)
+	  {
+	    tre_tnfa_approx_reach_t *reach_p;
+	    int depth;
+	    int cost, cost0;
+	    tre_tnfa_transition_t *trans;
+
+	    /* Pop the first item off the deque. */
+	    reach_p = *deque_start;
+	    id = reach_p - reach_next;
+	    depth = reach_p->depth;
+
+	    /* Compute cost at current depth. */
+	    cost = reach_p->costs[depth][TRE_M_COST];
+	    if (reach_p->params.cost_del != TRE_PARAM_UNSET)
+	      cost += reach_p->params.cost_del;
+
+	    /* Check cost, number of deletes, and total number of errors
+	       at current depth. */
+	    if (cost > reach_p->params.max_cost
+		|| (reach_p->costs[depth][TRE_M_NUM_DEL] + 1
+		    > reach_p->params.max_del)
+		|| (reach_p->costs[depth][TRE_M_NUM_ERR] + 1
+		    > reach_p->params.max_err))
+	      {
+		/* Too many errors or cost too large. */
+		DPRINT(("  delete: from %03d: cost too large\n", id));
+		deque_start++;
+		if (deque_start >= (ringbuffer + 512))
+		  deque_start = ringbuffer;
+		continue;
+	      }
+
+	    /* Compute overall cost. */
+	    cost0 = cost;
+	    if (depth > 0)
+	      {
+		cost0 = reach_p->costs[0][TRE_M_COST];
+		if (reach_p->params.cost_del != TRE_PARAM_UNSET)
+		  cost0 += reach_p->params.cost_del;
+		else
+		  cost0 += default_params.cost_del;
+	      }
+
+	    for (trans = reach_p->state; trans->state; trans++)
+	      {
+		int dest_id = trans->state_id;
+		DPRINT(("  delete: from %03d to %03d, cost %d (%d): ",
+			id, dest_id, cost0, reach_p->params.max_cost));
+
+		if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
+		  {
+		    DPRINT(("assertion failed\n"));
+		    continue;
+		  }
+
+		/* Compute tag values after this transition. */
+		for (i = 0; i < num_tags; i++)
+		  tmp_tags[i] = reach_p->tags[i];
+		if (trans->tags)
+		  for (i = 0; trans->tags[i] >= 0; i++)
+		    if (trans->tags[i] < num_tags)
+		      tmp_tags[trans->tags[i]] = pos;
+
+		/* If another path has also reached this state, choose the one
+		   with the smallest cost or best tags if costs are equal. */
+		if (reach_next[dest_id].pos == pos
+		    && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
+			|| (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
+			    && (!match_tags
+				|| !tre_tag_order(num_tags,
+						  tnfa->tag_directions,
+						  tmp_tags,
+						  reach_next[dest_id].tags)))))
+		  {
+		    DPRINT(("lose, cost0 %d, have %d\n",
+			    cost0, reach_next[dest_id].costs[0][TRE_M_COST]));
+		    continue;
+		  }
+		DPRINT(("win\n"));
+
+		/* Set state, position, tags, parameters, depth, and costs. */
+		reach_next[dest_id].state = trans->state;
+		reach_next[dest_id].pos = pos;
+		for (i = 0; i < num_tags; i++)
+		  reach_next[dest_id].tags[i] = tmp_tags[i];
+
+		reach_next[dest_id].params = reach_p->params;
+		if (trans->params)
+		  tre_set_params(&reach_next[dest_id], trans->params,
+				 default_params);
+
+		reach_next[dest_id].depth = reach_p->depth;
+		memcpy(&reach_next[dest_id].costs,
+		       reach_p->costs,
+		       sizeof(reach_p->costs[0][0])
+		       * TRE_M_LAST * (depth + 1));
+		reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
+		reach_next[dest_id].costs[depth][TRE_M_NUM_DEL]++;
+		reach_next[dest_id].costs[depth][TRE_M_NUM_ERR]++;
+		if (depth > 0)
+		  {
+		    reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
+		    reach_next[dest_id].costs[0][TRE_M_NUM_DEL]++;
+		    reach_next[dest_id].costs[0][TRE_M_NUM_ERR]++;
+		  }
+
+		if (trans->state == tnfa->final
+		    && (match_eo < 0
+			|| match_costs[TRE_M_COST] > cost0
+			|| (match_costs[TRE_M_COST] == cost0
+			    && (num_tags > 0
+				&& tmp_tags[0] <= match_tags[0]))))
+		  {
+		    DPRINT(("	 setting new match at %d, cost %d\n",
+			    pos, cost0));
+		    match_eo = pos;
+		    memcpy(match_costs, reach_next[dest_id].costs[0],
+			   sizeof(match_costs[0]) * TRE_M_LAST);
+		    for (i = 0; i < num_tags; i++)
+		      match_tags[i] = tmp_tags[i];
+		  }
+
+		/* Add to the end of the deque. */
+		*deque_end = &reach_next[dest_id];
+		deque_end++;
+		if (deque_end >= (ringbuffer + 512))
+		  deque_end = ringbuffer;
+		assert(deque_end != deque_start);
+	      }
+	    deque_start++;
+	    if (deque_start >= (ringbuffer + 512))
+	      deque_start = ringbuffer;
+	  }
+
+      }
+
+#ifdef TRE_DEBUG
+      tre_print_reach(tnfa, reach_next, pos, num_tags);
+#endif /* TRE_DEBUG */
+
+      /* Check for end of string. */
+      if (len < 0)
+	{
+	  if (type == STR_USER)
+	    {
+	      if (str_user_end)
+		break;
+	    }
+	  else if (next_c == L'\0')
+	    break;
+	}
+      else
+	{
+	  if (pos >= len)
+	    break;
+	}
+
+      prev_pos = pos;
+      GET_NEXT_WCHAR();
+
+      /* Swap `reach' and `reach_next'. */
+      {
+	tre_tnfa_approx_reach_t *tmp;
+	tmp = reach;
+	reach = reach_next;
+	reach_next = tmp;
+      }
+
+      /* Handle exact matches and substitutions. */
+      for (id = 0; id < tnfa->num_states; id++)
+	{
+	  tre_tnfa_transition_t *trans;
+
+	  if (reach[id].pos < prev_pos)
+	    continue;  /* Not reached. */
+	  for (trans = reach[id].state; trans->state; trans++)
+	    {
+	      int dest_id;
+	      int depth;
+	      int cost, cost0, err;
+
+	      if (trans->assertions
+		  && (CHECK_ASSERTIONS(trans->assertions)
+		      || CHECK_CHAR_CLASSES(trans, tnfa, eflags)))
+		{
+		  DPRINT(("  exact,  from %d: assert failed\n", id));
+		  continue;
+		}
+
+	      depth = reach[id].depth;
+	      dest_id = trans->state_id;
+
+	      cost = reach[id].costs[depth][TRE_M_COST];
+	      cost0 = reach[id].costs[0][TRE_M_COST];
+	      err = 0;
+
+	      if (trans->code_min > (tre_cint_t)prev_c
+		  || trans->code_max < (tre_cint_t)prev_c)
+		{
+		  /* Handle substitutes.  The required character was not in
+		     the string, so match it in place of whatever was supposed
+		     to be there and increase costs accordingly. */
+		  err = 1;
+
+		  /* Compute and check cost at current depth. */
+		  cost = reach[id].costs[depth][TRE_M_COST];
+		  if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
+		    cost += reach[id].params.cost_subst;
+		  if (cost > reach[id].params.max_cost)
+		    continue; /* Cost too large. */
+
+		  /* Check number of substitutes at current depth. */
+		  if (reach[id].costs[depth][TRE_M_NUM_SUBST] + 1
+		      > reach[id].params.max_subst)
+		    continue; /* Too many substitutes. */
+
+		  /* Check total number of errors at current depth. */
+		  if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
+		      > reach[id].params.max_err)
+		    continue; /* Too many errors. */
+
+		  /* Compute overall cost. */
+		  cost0 = cost;
+		  if (depth > 0)
+		    {
+		      cost0 = reach[id].costs[0][TRE_M_COST];
+		      if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
+			cost0 += reach[id].params.cost_subst;
+		      else
+			cost0 += default_params.cost_subst;
+		    }
+		  DPRINT(("  subst,  from %03d to %03d, cost %d: ",
+			  id, dest_id, cost0));
+		}
+	      else
+		DPRINT(("  exact,  from %03d to %03d, cost %d: ",
+			id, dest_id, cost0));
+
+	      /* Compute tag values after this transition. */
+	      for (i = 0; i < num_tags; i++)
+		tmp_tags[i] = reach[id].tags[i];
+	      if (trans->tags)
+		for (i = 0; trans->tags[i] >= 0; i++)
+		  if (trans->tags[i] < num_tags)
+		    tmp_tags[trans->tags[i]] = pos;
+
+	      /* If another path has also reached this state, choose the
+		 one with the smallest cost or best tags if costs are equal. */
+	      if (reach_next[dest_id].pos == pos
+		  && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
+		      || (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
+			  && !tre_tag_order(num_tags, tnfa->tag_directions,
+					    tmp_tags,
+					    reach_next[dest_id].tags))))
+		{
+		  DPRINT(("lose\n"));
+		  continue;
+		}
+	      DPRINT(("win %d %d\n",
+		      reach_next[dest_id].pos,
+		      reach_next[dest_id].costs[0][TRE_M_COST]));
+
+	      /* Set state, position, tags, and depth. */
+	      reach_next[dest_id].state = trans->state;
+	      reach_next[dest_id].pos = pos;
+	      for (i = 0; i < num_tags; i++)
+		reach_next[dest_id].tags[i] = tmp_tags[i];
+	      reach_next[dest_id].depth = reach[id].depth;
+
+	      /* Set parameters. */
+	      reach_next[dest_id].params = reach[id].params;
+	      if (trans->params)
+		tre_set_params(&reach_next[dest_id], trans->params,
+			       default_params);
+
+	      /* Set the costs after this transition. */
+		memcpy(&reach_next[dest_id].costs,
+		       reach[id].costs,
+		       sizeof(reach[id].costs[0][0])
+		       * TRE_M_LAST * (depth + 1));
+	      reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
+	      reach_next[dest_id].costs[depth][TRE_M_NUM_SUBST] += err;
+	      reach_next[dest_id].costs[depth][TRE_M_NUM_ERR] += err;
+	      if (depth > 0)
+		{
+		  reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
+		  reach_next[dest_id].costs[0][TRE_M_NUM_SUBST] += err;
+		  reach_next[dest_id].costs[0][TRE_M_NUM_ERR] += err;
+		}
+
+	      if (trans->state == tnfa->final
+		  && (match_eo < 0
+		      || cost0 < match_costs[TRE_M_COST]
+		      || (cost0 == match_costs[TRE_M_COST]
+			  && num_tags > 0 && tmp_tags[0] <= match_tags[0])))
+		{
+		  DPRINT(("    setting new match at %d, cost %d\n",
+			  pos, cost0));
+		  match_eo = pos;
+		  for (i = 0; i < TRE_M_LAST; i++)
+		    match_costs[i] = reach_next[dest_id].costs[0][i];
+		  for (i = 0; i < num_tags; i++)
+		    match_tags[i] = tmp_tags[i];
+		}
+	    }
+	}
+    }
+
+  DPRINT(("match end offset = %d, match cost = %d\n", match_eo,
+	  match_costs[TRE_M_COST]));
+
+#ifndef TRE_USE_ALLOCA
+  if (buf)
+    xfree(buf);
+#endif /* !TRE_USE_ALLOCA */
+
+  match->cost = match_costs[TRE_M_COST];
+  match->num_ins = match_costs[TRE_M_NUM_INS];
+  match->num_del = match_costs[TRE_M_NUM_DEL];
+  match->num_subst = match_costs[TRE_M_NUM_SUBST];
+  *match_end_ofs = match_eo;
+
+  return match_eo >= 0 ? REG_OK : REG_NOMATCH;
+}
+/*
+  tre-match-backtrack.c - TRE backtracking regex matching engine
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  This matcher is for regexps that use back referencing.  Regexp matching
+  with back referencing is an NP-complete problem on the number of back
+  references.  The easiest way to match them is to use a backtracking
+  routine which basically goes through all possible paths in the TNFA
+  and chooses the one which results in the best (leftmost and longest)
+  match.  This can be spectacularly expensive and may run out of stack
+  space, but there really is no better known generic algorithm.	 Quoting
+  Henry Spencer from comp.compilers:
+  <URL: http://compilers.iecc.com/comparch/article/93-03-102>
+
+    POSIX.2 REs require longest match, which is really exciting to
+    implement since the obsolete ("basic") variant also includes
+    \<digit>.  I haven't found a better way of tackling this than doing
+    a preliminary match using a DFA (or simulation) on a modified RE
+    that just replicates subREs for \<digit>, and then doing a
+    backtracking match to determine whether the subRE matches were
+    right.  This can be rather slow, but I console myself with the
+    thought that people who use \<digit> deserve very slow execution.
+    (Pun unintentional but very appropriate.)
+
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef TRE_USE_ALLOCA
+/* AIX requires this to be the first thing in the file.	 */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+#endif /* TRE_USE_ALLOCA */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* HAVE_WCTYPE_H */
+#ifndef TRE_WCHAR
+#include <ctype.h>
+#endif /* !TRE_WCHAR */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+
+
+typedef struct {
+  int pos;
+  const char *str_byte;
+#ifdef TRE_WCHAR
+  const wchar_t *str_wide;
+#endif /* TRE_WCHAR */
+  tre_tnfa_transition_t *state;
+  int state_id;
+  int next_c;
+  int *tags;
+#ifdef TRE_MBSTATE
+  mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+} tre_backtrack_item_t;
+
+typedef struct tre_backtrack_struct {
+  tre_backtrack_item_t item;
+  struct tre_backtrack_struct *prev;
+  struct tre_backtrack_struct *next;
+} *tre_backtrack_t;
+
+#ifdef TRE_WCHAR
+#define BT_STACK_WIDE_IN(_str_wide)	stack->item.str_wide = (_str_wide)
+#define BT_STACK_WIDE_OUT		(str_wide) = stack->item.str_wide
+#else /* !TRE_WCHAR */
+#define BT_STACK_WIDE_IN(_str_wide)
+#define BT_STACK_WIDE_OUT
+#endif /* !TRE_WCHAR */
+
+#ifdef TRE_MBSTATE
+#define BT_STACK_MBSTATE_IN  stack->item.mbstate = (mbstate)
+#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
+#else /* !TRE_MBSTATE */
+#define BT_STACK_MBSTATE_IN
+#define BT_STACK_MBSTATE_OUT
+#endif /* !TRE_MBSTATE */
+
+
+#ifdef TRE_USE_ALLOCA
+#define tre_bt_mem_new		  tre_mem_newa
+#define tre_bt_mem_alloc	  tre_mem_alloca
+#define tre_bt_mem_destroy(obj)	  do { } while (0)
+#else /* !TRE_USE_ALLOCA */
+#define tre_bt_mem_new		  tre_mem_new
+#define tre_bt_mem_alloc	  tre_mem_alloc
+#define tre_bt_mem_destroy	  tre_mem_destroy
+#endif /* !TRE_USE_ALLOCA */
+
+
+#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
+  do									      \
+    {									      \
+      int i;								      \
+      if (!stack->next)							      \
+	{								      \
+	  tre_backtrack_t s;						      \
+	  s = tre_bt_mem_alloc(mem, sizeof(*s));			      \
+	  if (!s)							      \
+	    {								      \
+	      tre_bt_mem_destroy(mem);					      \
+	      if (tags)							      \
+		xfree(tags);						      \
+	      if (pmatch)						      \
+		xfree(pmatch);						      \
+	      if (states_seen)						      \
+		xfree(states_seen);					      \
+	      return REG_ESPACE;					      \
+	    }								      \
+	  s->prev = stack;						      \
+	  s->next = NULL;						      \
+	  s->item.tags = tre_bt_mem_alloc(mem,				      \
+					  sizeof(*tags) * tnfa->num_tags);    \
+	  if (!s->item.tags)						      \
+	    {								      \
+	      tre_bt_mem_destroy(mem);					      \
+	      if (tags)							      \
+		xfree(tags);						      \
+	      if (pmatch)						      \
+		xfree(pmatch);						      \
+	      if (states_seen)						      \
+		xfree(states_seen);					      \
+	      return REG_ESPACE;					      \
+	    }								      \
+	  stack->next = s;						      \
+	  stack = s;							      \
+	}								      \
+      else								      \
+	stack = stack->next;						      \
+      stack->item.pos = (_pos);						      \
+      stack->item.str_byte = (_str_byte);				      \
+      BT_STACK_WIDE_IN(_str_wide);					      \
+      stack->item.state = (_state);					      \
+      stack->item.state_id = (_state_id);				      \
+      stack->item.next_c = (_next_c);					      \
+      for (i = 0; i < tnfa->num_tags; i++)				      \
+	stack->item.tags[i] = (_tags)[i];				      \
+      BT_STACK_MBSTATE_IN;						      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#define BT_STACK_POP()							      \
+  do									      \
+    {									      \
+      int i;								      \
+      assert(stack->prev);						      \
+      pos = stack->item.pos;						      \
+      if (type == STR_USER)                                                   \
+        str_source->rewind(pos + pos_add_next, str_source->context);          \
+      str_byte = stack->item.str_byte;					      \
+      BT_STACK_WIDE_OUT;						      \
+      state = stack->item.state;					      \
+      next_c = stack->item.next_c;					      \
+      for (i = 0; i < tnfa->num_tags; i++)				      \
+	tags[i] = stack->item.tags[i];					      \
+      BT_STACK_MBSTATE_OUT;						      \
+      stack = stack->prev;						      \
+    }									      \
+  while (/*CONSTCOND*/0)
+
+#undef MIN
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+
+reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+		       int len, tre_str_type_t type, int *match_tags,
+		       int eflags, int *match_end_ofs)
+{
+  /* State variables required by GET_NEXT_WCHAR. */
+  tre_char_t prev_c = 0, next_c = 0;
+  const char *str_byte = string;
+  int pos = 0;
+  unsigned int pos_add_next = 1;
+#ifdef TRE_WCHAR
+  const wchar_t *str_wide = string;
+#ifdef TRE_MBSTATE
+  mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+#endif /* TRE_WCHAR */
+  int reg_notbol = eflags & REG_NOTBOL;
+  int reg_noteol = eflags & REG_NOTEOL;
+  int reg_newline = tnfa->cflags & REG_NEWLINE;
+  int str_user_end = 0;
+
+  /* These are used to remember the necessary values of the above
+     variables to return to the position where the current search
+     started from. */
+  int next_c_start;
+  const char *str_byte_start;
+  int pos_start = -1;
+#ifdef TRE_WCHAR
+  const wchar_t *str_wide_start;
+#endif /* TRE_WCHAR */
+#ifdef TRE_MBSTATE
+  mbstate_t mbstate_start;
+#endif /* TRE_MBSTATE */
+
+  /* End offset of best match so far, or -1 if no match found yet. */
+  int match_eo = -1;
+  /* Tag arrays. */
+  int *next_tags, *tags = NULL;
+  /* Current TNFA state. */
+  tre_tnfa_transition_t *state;
+  int *states_seen = NULL;
+
+  /* Memory allocator to for allocating the backtracking stack. */
+  tre_mem_t mem = tre_bt_mem_new();
+
+  /* The backtracking stack. */
+  tre_backtrack_t stack;
+
+  tre_tnfa_transition_t *trans_i;
+  regmatch_t *pmatch = NULL;
+  int ret;
+
+#ifdef TRE_MBSTATE
+  memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+  if (!mem)
+    return REG_ESPACE;
+  stack = tre_bt_mem_alloc(mem, sizeof(*stack));
+  if (!stack)
+    {
+      ret = REG_ESPACE;
+      goto error_exit;
+    }
+  stack->prev = NULL;
+  stack->next = NULL;
+
+  DPRINT(("tnfa_execute_backtrack, input type %d\n", type));
+  DPRINT(("len = %d\n", len));
+
+#ifdef TRE_USE_ALLOCA
+  tags = alloca(sizeof(*tags) * tnfa->num_tags);
+  pmatch = alloca(sizeof(*pmatch) * tnfa->num_submatches);
+  states_seen = alloca(sizeof(*states_seen) * tnfa->num_states);
+#else /* !TRE_USE_ALLOCA */
+  if (tnfa->num_tags)
+    {
+      tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+      if (!tags)
+	{
+	  ret = REG_ESPACE;
+	  goto error_exit;
+	}
+    }
+  if (tnfa->num_submatches)
+    {
+      pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches);
+      if (!pmatch)
+	{
+	  ret = REG_ESPACE;
+	  goto error_exit;
+	}
+    }
+  if (tnfa->num_states)
+    {
+      states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states);
+      if (!states_seen)
+	{
+	  ret = REG_ESPACE;
+	  goto error_exit;
+	}
+    }
+#endif /* !TRE_USE_ALLOCA */
+
+ retry:
+  {
+    int i;
+    for (i = 0; i < tnfa->num_tags; i++)
+      {
+	tags[i] = -1;
+	if (match_tags)
+	  match_tags[i] = -1;
+      }
+    for (i = 0; i < tnfa->num_states; i++)
+      states_seen[i] = 0;
+  }
+
+  state = NULL;
+  pos = pos_start;
+  if (type == STR_USER)
+    str_source->rewind(pos + pos_add_next, str_source->context);
+  GET_NEXT_WCHAR();
+  pos_start = pos;
+  next_c_start = next_c;
+  str_byte_start = str_byte;
+#ifdef TRE_WCHAR
+  str_wide_start = str_wide;
+#endif /* TRE_WCHAR */
+#ifdef TRE_MBSTATE
+  mbstate_start = mbstate;
+#endif /* TRE_MBSTATE */
+
+  /* Handle initial states. */
+  next_tags = NULL;
+  for (trans_i = tnfa->initial; trans_i->state; trans_i++)
+    {
+      DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c));
+      if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
+	{
+	  DPRINT(("assert failed\n"));
+	  continue;
+	}
+      if (state == NULL)
+	{
+	  /* Start from this state. */
+	  state = trans_i->state;
+	  next_tags = trans_i->tags;
+	}
+      else
+	{
+	  /* Backtrack to this state. */
+	  DPRINT(("saving state %d for backtracking\n", trans_i->state_id));
+	  BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state,
+			trans_i->state_id, next_c, tags, mbstate);
+	  {
+	    int *tmp = trans_i->tags;
+	    if (tmp)
+	      while (*tmp >= 0)
+		stack->item.tags[*tmp++] = pos;
+	  }
+	}
+    }
+
+  if (next_tags)
+    for (; *next_tags >= 0; next_tags++)
+      tags[*next_tags] = pos;
+
+
+  DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte));
+  DPRINT(("pos:chr/code | state and tags\n"));
+  DPRINT(("-------------+------------------------------------------------\n"));
+
+  if (state == NULL)
+    goto backtrack;
+
+  while (/*CONSTCOND*/1)
+    {
+      tre_tnfa_transition_t *next_state;
+      int empty_br_match;
+
+      DPRINT(("start loop\n"));
+      if (state == tnfa->final)
+	{
+	  DPRINT(("  match found, %d %d\n", match_eo, pos));
+	  if (match_eo < pos
+	      || (match_eo == pos
+		  && match_tags
+		  && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
+				   tags, match_tags)))
+	    {
+	      int i;
+	      /* This match wins the previous match. */
+	      DPRINT(("	 win previous\n"));
+	      match_eo = pos;
+	      if (match_tags)
+		for (i = 0; i < tnfa->num_tags; i++)
+		  match_tags[i] = tags[i];
+	    }
+	  /* Our TNFAs never have transitions leaving from the final state,
+	     so we jump right to backtracking. */
+	  goto backtrack;
+	}
+
+#ifdef TRE_DEBUG
+      DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c,
+	      state));
+      {
+	int i;
+	for (i = 0; i < tnfa->num_tags; i++)
+	  DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : ""));
+	DPRINT(("\n"));
+      }
+#endif /* TRE_DEBUG */
+
+      /* Go to the next character in the input string. */
+      empty_br_match = 0;
+      trans_i = state;
+      if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
+	{
+	  /* This is a back reference state.  All transitions leaving from
+	     this state have the same back reference "assertion".  Instead
+	     of reading the next character, we match the back reference. */
+	  int so, eo, bt = trans_i->u.backref;
+	  int bt_len;
+	  int result;
+
+	  DPRINT(("  should match back reference %d\n", bt));
+	  /* Get the substring we need to match against.  Remember to
+	     turn off REG_NOSUB temporarily. */
+	  tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & /*LINTED*/!REG_NOSUB,
+			  tnfa, tags, pos);
+	  so = pmatch[bt].rm_so;
+	  eo = pmatch[bt].rm_eo;
+	  bt_len = eo - so;
+
+#ifdef TRE_DEBUG
+	  {
+	    int slen;
+	    if (len < 0)
+	      slen = bt_len;
+	    else
+	      slen = MIN(bt_len, len - pos);
+
+	    if (type == STR_BYTE)
+	      {
+		DPRINT(("  substring (len %d) is [%d, %d[: '%.*s'\n",
+			bt_len, so, eo, bt_len, (char*)string + so));
+		DPRINT(("  current string is '%.*s'\n", slen, str_byte - 1));
+	      }
+#ifdef TRE_WCHAR
+	    else if (type == STR_WIDE)
+	      {
+		DPRINT(("  substring (len %d) is [%d, %d[: '%.*" STRF "'\n",
+			bt_len, so, eo, bt_len, (wchar_t*)string + so));
+		DPRINT(("  current string is '%.*" STRF "'\n",
+			slen, str_wide - 1));
+	      }
+#endif /* TRE_WCHAR */
+	  }
+#endif
+
+	  if (len < 0)
+	    {
+	      if (type == STR_USER)
+		result = str_source->compare((unsigned)so, (unsigned)pos,
+					     (unsigned)bt_len,
+					     str_source->context);
+#ifdef TRE_WCHAR
+	      else if (type == STR_WIDE)
+		result = wcsncmp((const wchar_t*)string + so, str_wide - 1,
+				 (size_t)bt_len);
+#endif /* TRE_WCHAR */
+	      else
+		result = strncmp((const char*)string + so, str_byte - 1,
+				 (size_t)bt_len);
+	    }
+	  else if (len - pos < bt_len)
+	    result = 1;
+#ifdef TRE_WCHAR
+	  else if (type == STR_WIDE)
+	    result = wmemcmp((const wchar_t*)string + so, str_wide - 1,
+			     (size_t)bt_len);
+#endif /* TRE_WCHAR */
+	  else
+	    result = memcmp((const char*)string + so, str_byte - 1,
+			    (size_t)bt_len);
+
+	  if (result == 0)
+	    {
+	      /* Back reference matched.  Check for infinite loop. */
+	      if (bt_len == 0)
+		empty_br_match = 1;
+	      if (empty_br_match && states_seen[trans_i->state_id])
+		{
+		  DPRINT(("  avoid loop\n"));
+		  goto backtrack;
+		}
+
+	      states_seen[trans_i->state_id] = empty_br_match;
+
+	      /* Advance in input string and resync `prev_c', `next_c'
+		 and pos. */
+	      DPRINT(("	 back reference matched\n"));
+	      str_byte += bt_len - 1;
+#ifdef TRE_WCHAR
+	      str_wide += bt_len - 1;
+#endif /* TRE_WCHAR */
+	      pos += bt_len - 1;
+	      GET_NEXT_WCHAR();
+	      DPRINT(("	 pos now %d\n", pos));
+	    }
+	  else
+	    {
+	      DPRINT(("	 back reference did not match\n"));
+	      goto backtrack;
+	    }
+	}
+      else
+	{
+	  /* Check for end of string. */
+	  if (len < 0)
+	    {
+	      if (type == STR_USER)
+		{
+		  if (str_user_end)
+		    goto backtrack;
+		}
+	      else if (next_c == L'\0')
+		goto backtrack;
+	    }
+	  else
+	    {
+	      if (pos >= len)
+		goto backtrack;
+	    }
+
+	  /* Read the next character. */
+	  GET_NEXT_WCHAR();
+	}
+
+      next_state = NULL;
+      for (trans_i = state; trans_i->state; trans_i++)
+	{
+	  DPRINT(("  transition %d-%d (%c-%c) %d to %d\n",
+		  trans_i->code_min, trans_i->code_max,
+		  trans_i->code_min, trans_i->code_max,
+		  trans_i->assertions, trans_i->state_id));
+	  if (trans_i->code_min <= (tre_cint_t)prev_c
+	      && trans_i->code_max >= (tre_cint_t)prev_c)
+	    {
+	      if (trans_i->assertions
+		  && (CHECK_ASSERTIONS(trans_i->assertions)
+		      || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+		{
+		  DPRINT(("  assertion failed\n"));
+		  continue;
+		}
+
+	      if (next_state == NULL)
+		{
+		  /* First matching transition. */
+		  DPRINT(("  Next state is %d\n", trans_i->state_id));
+		  next_state = trans_i->state;
+		  next_tags = trans_i->tags;
+		}
+	      else
+		{
+		  /* Second matching transition.  We may need to backtrack here
+		     to take this transition instead of the first one, so we
+		     push this transition in the backtracking stack so we can
+		     jump back here if needed. */
+		  DPRINT(("  saving state %d for backtracking\n",
+			  trans_i->state_id));
+		  BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state,
+				trans_i->state_id, next_c, tags, mbstate);
+		  {
+		    int *tmp;
+		    for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
+		      stack->item.tags[*tmp] = pos;
+		  }
+#if 0 /* XXX - it's important not to look at all transitions here to keep
+	 the stack small! */
+		  break;
+#endif
+		}
+	    }
+	}
+
+      if (next_state != NULL)
+	{
+	  /* Matching transitions were found.  Take the first one. */
+	  state = next_state;
+
+	  /* Update the tag values. */
+	  if (next_tags)
+	    while (*next_tags >= 0)
+	      tags[*next_tags++] = pos;
+	}
+      else
+	{
+	backtrack:
+	  /* A matching transition was not found.  Try to backtrack. */
+	  if (stack->prev)
+	    {
+	      DPRINT(("	 backtracking\n"));
+	      if (stack->item.state->assertions && ASSERT_BACKREF)
+		{
+		  DPRINT(("  states_seen[%d] = 0\n",
+			  stack->item.state_id));
+		  states_seen[stack->item.state_id] = 0;
+		}
+
+	      BT_STACK_POP();
+	    }
+	  else if (match_eo < 0)
+	    {
+	      /* Try starting from a later position in the input string. */
+	      /* Check for end of string. */
+	      if (len < 0)
+		{
+		  if (next_c == L'\0')
+		    {
+		      DPRINT(("end of string.\n"));
+		      break;
+		    }
+		}
+	      else
+		{
+		  if (pos >= len)
+		    {
+		      DPRINT(("end of string.\n"));
+		      break;
+		    }
+		}
+	      DPRINT(("restarting from next start position\n"));
+	      next_c = next_c_start;
+#ifdef TRE_MBSTATE
+	      mbstate = mbstate_start;
+#endif /* TRE_MBSTATE */
+	      str_byte = str_byte_start;
+#ifdef TRE_WCHAR
+	      str_wide = str_wide_start;
+#endif /* TRE_WCHAR */
+	      goto retry;
+	    }
+	  else
+	    {
+	      DPRINT(("finished\n"));
+	      break;
+	    }
+	}
+    }
+
+  ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+  *match_end_ofs = match_eo;
+
+ error_exit:
+  tre_bt_mem_destroy(mem);
+#ifndef TRE_USE_ALLOCA
+  if (tags)
+    xfree(tags);
+  if (pmatch)
+    xfree(pmatch);
+  if (states_seen)
+    xfree(states_seen);
+#endif /* !TRE_USE_ALLOCA */
+
+  return ret;
+}
+/*
+  tre-match-parallel.c - TRE parallel regex matching engine
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  This algorithm searches for matches basically by reading characters
+  in the searched string one by one, starting at the beginning.	 All
+  matching paths in the TNFA are traversed in parallel.	 When two or
+  more paths reach the same state, exactly one is chosen according to
+  tag ordering rules; if returning submatches is not required it does
+  not matter which path is chosen.
+
+  The worst case time required for finding the leftmost and longest
+  match, or determining that there is no match, is always linearly
+  dependent on the length of the text being searched.
+
+  This algorithm cannot handle TNFAs with back referencing nodes.
+  See `tre-match-backtrack.c'.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef TRE_USE_ALLOCA
+/* AIX requires this to be the first thing in the file.	 */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+#endif /* TRE_USE_ALLOCA */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif /* HAVE_WCHAR_H */
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif /* HAVE_WCTYPE_H */
+#ifndef TRE_WCHAR
+#include <ctype.h>
+#endif /* !TRE_WCHAR */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+
+
+
+
+typedef struct {
+  tre_tnfa_transition_t *state;
+  int *tags;
+} tre_tnfa_reach_t;
+
+typedef struct {
+  int pos;
+  int **tags;
+} tre_reach_pos_t;
+
+
+#ifdef TRE_DEBUG
+static void
+tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_reach_t *reach, int num_tags)
+{
+  int i;
+
+  while (reach->state != NULL)
+    {
+      DPRINT((" %p", (void *)reach->state));
+      if (num_tags > 0)
+	{
+	  DPRINT(("/"));
+	  for (i = 0; i < num_tags; i++)
+	    {
+	      DPRINT(("%d:%d", i, reach->tags[i]));
+	      if (i < (num_tags-1))
+		DPRINT((","));
+	    }
+	}
+      reach++;
+    }
+  DPRINT(("\n"));
+
+}
+#endif /* TRE_DEBUG */
+
+reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
+		      tre_str_type_t type, int *match_tags, int eflags,
+		      int *match_end_ofs)
+{
+  /* State variables required by GET_NEXT_WCHAR. */
+  tre_char_t prev_c = 0, next_c = 0;
+  const char *str_byte = string;
+  int pos = -1;
+  unsigned int pos_add_next = 1;
+#ifdef TRE_WCHAR
+  const wchar_t *str_wide = string;
+#ifdef TRE_MBSTATE
+  mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+#endif /* TRE_WCHAR */
+  int reg_notbol = eflags & REG_NOTBOL;
+  int reg_noteol = eflags & REG_NOTEOL;
+  int reg_newline = tnfa->cflags & REG_NEWLINE;
+  int str_user_end = 0;
+
+  char *buf;
+  tre_tnfa_transition_t *trans_i;
+  tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
+  tre_reach_pos_t *reach_pos;
+  int *tag_i;
+  int num_tags, i;
+
+  int match_eo = -1;	   /* end offset of match (-1 if no match found yet) */
+  int new_match = 0;
+  int *tmp_tags = NULL;
+  int *tmp_iptr;
+
+#ifdef TRE_MBSTATE
+  memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+  DPRINT(("tre_tnfa_run_parallel, input type %d\n", type));
+
+  if (!match_tags)
+    num_tags = 0;
+  else
+    num_tags = tnfa->num_tags;
+
+  /* Allocate memory for temporary data required for matching.	This needs to
+     be done for every matching operation to be thread safe.  This allocates
+     everything in a single large block from the stack frame using alloca()
+     or with malloc() if alloca is unavailable. */
+  {
+    int tbytes, rbytes, pbytes, xbytes, total_bytes;
+    char *tmp_buf;
+    /* Compute the length of the block we need. */
+    tbytes = sizeof(*tmp_tags) * num_tags;
+    rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
+    pbytes = sizeof(*reach_pos) * tnfa->num_states;
+    xbytes = sizeof(int) * num_tags;
+    total_bytes =
+      (sizeof(long) - 1) * 4 /* for alignment paddings */
+      + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes;
+
+    /* Allocate the memory. */
+#ifdef TRE_USE_ALLOCA
+    buf = alloca(total_bytes);
+#else /* !TRE_USE_ALLOCA */
+    buf = xmalloc((unsigned)total_bytes);
+#endif /* !TRE_USE_ALLOCA */
+    if (buf == NULL)
+      return REG_ESPACE;
+    memset(buf, 0, (size_t)total_bytes);
+
+    /* Get the various pointers within tmp_buf (properly aligned). */
+    tmp_tags = (void *)buf;
+    tmp_buf = buf + tbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach_next = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach = (void *)tmp_buf;
+    tmp_buf += rbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    reach_pos = (void *)tmp_buf;
+    tmp_buf += pbytes;
+    tmp_buf += ALIGN(tmp_buf, long);
+    for (i = 0; i < tnfa->num_states; i++)
+      {
+	reach[i].tags = (void *)tmp_buf;
+	tmp_buf += xbytes;
+	reach_next[i].tags = (void *)tmp_buf;
+	tmp_buf += xbytes;
+      }
+  }
+
+  for (i = 0; i < tnfa->num_states; i++)
+    reach_pos[i].pos = -1;
+
+  /* If only one character can start a match, find it first. */
+  if (tnfa->first_char >= 0 && type == STR_BYTE && str_byte)
+    {
+      const char *orig_str = str_byte;
+      int first = tnfa->first_char;
+
+      if (len >= 0)
+	str_byte = memchr(orig_str, first, (size_t)len);
+      else
+	str_byte = strchr(orig_str, first);
+      if (str_byte == NULL)
+	{
+#ifndef TRE_USE_ALLOCA
+	  if (buf)
+	    xfree(buf);
+#endif /* !TRE_USE_ALLOCA */
+	  return REG_NOMATCH;
+	}
+      DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte - orig_str)));
+      if (str_byte >= orig_str + 1)
+	prev_c = (unsigned char)*(str_byte - 1);
+      next_c = (unsigned char)*str_byte;
+      pos = str_byte - orig_str;
+      if (len < 0 || pos < len)
+	str_byte++;
+    }
+  else
+    {
+      GET_NEXT_WCHAR();
+      pos = 0;
+    }
+
+#if 0
+  /* Skip over characters that cannot possibly be the first character
+     of a match. */
+  if (tnfa->firstpos_chars != NULL)
+    {
+      char *chars = tnfa->firstpos_chars;
+
+      if (len < 0)
+	{
+	  const char *orig_str = str_byte;
+	  /* XXX - use strpbrk() and wcspbrk() because they might be
+	     optimized for the target architecture.  Try also strcspn()
+	     and wcscspn() and compare the speeds. */
+	  while (next_c != L'\0' && !chars[next_c])
+	    {
+	      next_c = *str_byte++;
+	    }
+	  prev_c = *(str_byte - 2);
+	  pos += str_byte - orig_str;
+	  DPRINT(("skipped %d chars\n", str_byte - orig_str));
+	}
+      else
+	{
+	  while (pos <= len && !chars[next_c])
+	    {
+	      prev_c = next_c;
+	      next_c = (unsigned char)(*str_byte++);
+	      pos++;
+	    }
+	}
+    }
+#endif
+
+  DPRINT(("length: %d\n", len));
+  DPRINT(("pos:chr/code | states and tags\n"));
+  DPRINT(("-------------+------------------------------------------------\n"));
+
+  reach_next_i = reach_next;
+  while (/*CONSTCOND*/1)
+    {
+      /* If no match found yet, add the initial states to `reach_next'. */
+      if (match_eo < 0)
+	{
+	  DPRINT((" init >"));
+	  trans_i = tnfa->initial;
+	  while (trans_i->state != NULL)
+	    {
+	      if (reach_pos[trans_i->state_id].pos < pos)
+		{
+		  if (trans_i->assertions
+		      && CHECK_ASSERTIONS(trans_i->assertions))
+		    {
+		      DPRINT(("assertion failed\n"));
+		      trans_i++;
+		      continue;
+		    }
+
+		  DPRINT((" %p", (void *)trans_i->state));
+		  reach_next_i->state = trans_i->state;
+		  for (i = 0; i < num_tags; i++)
+		    reach_next_i->tags[i] = -1;
+		  tag_i = trans_i->tags;
+		  if (tag_i)
+		    while (*tag_i >= 0)
+		      {
+			if (*tag_i < num_tags)
+			  reach_next_i->tags[*tag_i] = pos;
+			tag_i++;
+		      }
+		  if (reach_next_i->state == tnfa->final)
+		    {
+		      DPRINT(("	 found empty match\n"));
+		      match_eo = pos;
+		      new_match = 1;
+		      for (i = 0; i < num_tags; i++)
+			match_tags[i] = reach_next_i->tags[i];
+		    }
+		  reach_pos[trans_i->state_id].pos = pos;
+		  reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+		  reach_next_i++;
+		}
+	      trans_i++;
+	    }
+	  DPRINT(("\n"));
+	  reach_next_i->state = NULL;
+	}
+      else
+	{
+	  if (num_tags == 0 || reach_next_i == reach_next)
+	    /* We have found a match. */
+	    break;
+	}
+
+      /* Check for end of string. */
+      if (len < 0)
+	{
+	  if (type == STR_USER)
+	    {
+	      if (str_user_end)
+		break;
+	    }
+	  else if (next_c == L'\0')
+	    break;
+	}
+      else
+	{
+	  if (pos >= len)
+	    break;
+	}
+
+      GET_NEXT_WCHAR();
+
+#ifdef TRE_DEBUG
+      DPRINT(("%3d:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c));
+      tre_print_reach(tnfa, reach_next, num_tags);
+      DPRINT(("%3d:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c));
+      tre_print_reach(tnfa, reach_next, num_tags);
+#endif /* TRE_DEBUG */
+
+      /* Swap `reach' and `reach_next'. */
+      reach_i = reach;
+      reach = reach_next;
+      reach_next = reach_i;
+
+      /* For each state in `reach', weed out states that don't fulfill the
+	 minimal matching conditions. */
+      if (tnfa->num_minimals && new_match)
+	{
+	  new_match = 0;
+	  reach_next_i = reach_next;
+	  for (reach_i = reach; reach_i->state; reach_i++)
+	    {
+	      int skip = 0;
+	      for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+		{
+		  int end = tnfa->minimal_tags[i];
+		  int start = tnfa->minimal_tags[i + 1];
+		  DPRINT(("  Minimal start %d, end %d\n", start, end));
+		  if (end >= num_tags)
+		    {
+		      DPRINT(("	 Throwing %p out.\n", reach_i->state));
+		      skip = 1;
+		      break;
+		    }
+		  else if (reach_i->tags[start] == match_tags[start]
+			   && reach_i->tags[end] < match_tags[end])
+		    {
+		      DPRINT(("	 Throwing %p out because t%d < %d\n",
+			      reach_i->state, end, match_tags[end]));
+		      skip = 1;
+		      break;
+		    }
+		}
+	      if (!skip)
+		{
+		  reach_next_i->state = reach_i->state;
+		  tmp_iptr = reach_next_i->tags;
+		  reach_next_i->tags = reach_i->tags;
+		  reach_i->tags = tmp_iptr;
+		  reach_next_i++;
+		}
+	    }
+	  reach_next_i->state = NULL;
+
+	  /* Swap `reach' and `reach_next'. */
+	  reach_i = reach;
+	  reach = reach_next;
+	  reach_next = reach_i;
+	}
+
+      /* For each state in `reach' see if there is a transition leaving with
+	 the current input symbol to a state not yet in `reach_next', and
+	 add the destination states to `reach_next'. */
+      reach_next_i = reach_next;
+      for (reach_i = reach; reach_i->state; reach_i++)
+	{
+	  for (trans_i = reach_i->state; trans_i->state; trans_i++)
+	    {
+	      /* Does this transition match the input symbol? */
+	      if (trans_i->code_min <= (tre_cint_t)prev_c &&
+		  trans_i->code_max >= (tre_cint_t)prev_c)
+		{
+		  if (trans_i->assertions
+		      && (CHECK_ASSERTIONS(trans_i->assertions)
+			  || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+		    {
+		      DPRINT(("assertion failed\n"));
+		      continue;
+		    }
+
+		  /* Compute the tags after this transition. */
+		  for (i = 0; i < num_tags; i++)
+		    tmp_tags[i] = reach_i->tags[i];
+		  tag_i = trans_i->tags;
+		  if (tag_i != NULL)
+		    while (*tag_i >= 0)
+		      {
+			if (*tag_i < num_tags)
+			  tmp_tags[*tag_i] = pos;
+			tag_i++;
+		      }
+
+		  if (reach_pos[trans_i->state_id].pos < pos)
+		    {
+		      /* Found an unvisited node. */
+		      reach_next_i->state = trans_i->state;
+		      tmp_iptr = reach_next_i->tags;
+		      reach_next_i->tags = tmp_tags;
+		      tmp_tags = tmp_iptr;
+		      reach_pos[trans_i->state_id].pos = pos;
+		      reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+
+		      if (reach_next_i->state == tnfa->final
+			  && (match_eo == -1
+			      || (num_tags > 0
+				  && reach_next_i->tags[0] <= match_tags[0])))
+			{
+			  DPRINT(("  found match %p\n", trans_i->state));
+			  match_eo = pos;
+			  new_match = 1;
+			  for (i = 0; i < num_tags; i++)
+			    match_tags[i] = reach_next_i->tags[i];
+			}
+		      reach_next_i++;
+
+		    }
+		  else
+		    {
+		      assert(reach_pos[trans_i->state_id].pos == pos);
+		      /* Another path has also reached this state.  We choose
+			 the winner by examining the tag values for both
+			 paths. */
+		      if (tre_tag_order(num_tags, tnfa->tag_directions,
+					tmp_tags,
+					*reach_pos[trans_i->state_id].tags))
+			{
+			  /* The new path wins. */
+			  tmp_iptr = *reach_pos[trans_i->state_id].tags;
+			  *reach_pos[trans_i->state_id].tags = tmp_tags;
+			  if (trans_i->state == tnfa->final)
+			    {
+			      DPRINT(("	 found better match\n"));
+			      match_eo = pos;
+			      new_match = 1;
+			      for (i = 0; i < num_tags; i++)
+				match_tags[i] = tmp_tags[i];
+			    }
+			  tmp_tags = tmp_iptr;
+			}
+		    }
+		}
+	    }
+	}
+      reach_next_i->state = NULL;
+    }
+
+  DPRINT(("match end offset = %d\n", match_eo));
+
+#ifndef TRE_USE_ALLOCA
+  if (buf)
+    xfree(buf);
+#endif /* !TRE_USE_ALLOCA */
+
+  *match_end_ofs = match_eo;
+  return match_eo >= 0 ? REG_OK : REG_NOMATCH;
+}
+
+/* EOF */
+/*
+  tre-mem.c - TRE memory allocator
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  This memory allocator is for allocating small memory blocks efficiently
+  in terms of memory overhead and execution speed.  The allocated blocks
+  cannot be freed individually, only all at once.  There can be multiple
+  allocators, though.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <stdlib.h>
+#include <string.h>
+
+
+
+/* Returns a new memory allocator or NULL if out of memory. */
+tre_mem_t
+tre_mem_new_impl(int provided, void *provided_block)
+{
+  tre_mem_t mem;
+  if (provided)
+    {
+      mem = provided_block;
+      memset(mem, 0, sizeof(*mem));
+    }
+  else
+    mem = xcalloc(1, sizeof(*mem));
+  if (mem == NULL)
+    return NULL;
+  return mem;
+}
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+void
+tre_mem_destroy(tre_mem_t mem)
+{
+  tre_list_t *tmp, *l = mem->blocks;
+
+  while (l != NULL)
+    {
+      xfree(l->data);
+      tmp = l->next;
+      xfree(l);
+      l = tmp;
+    }
+  xfree(mem);
+}
+
+
+/* Allocates a block of `size' bytes from `mem'.  Returns a pointer to the
+   allocated block or NULL if an underlying malloc() failed. */
+void *
+tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+		   int zero, size_t size)
+{
+  void *ptr;
+
+  if (mem->failed)
+    {
+      DPRINT(("tre_mem_alloc: oops, called after failure?!\n"));
+      return NULL;
+    }
+
+#ifdef MALLOC_DEBUGGING
+  if (!provided)
+    {
+      ptr = xmalloc(1);
+      if (ptr == NULL)
+	{
+	  DPRINT(("tre_mem_alloc: xmalloc forced failure\n"));
+	  mem->failed = 1;
+	  return NULL;
+	}
+      xfree(ptr);
+    }
+#endif /* MALLOC_DEBUGGING */
+
+  if (mem->n < size)
+    {
+      /* We need more memory than is available in the current block.
+	 Allocate a new block. */
+      tre_list_t *l;
+      if (provided)
+	{
+	  DPRINT(("tre_mem_alloc: using provided block\n"));
+	  if (provided_block == NULL)
+	    {
+	      DPRINT(("tre_mem_alloc: provided block was NULL\n"));
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  mem->ptr = provided_block;
+	  mem->n = TRE_MEM_BLOCK_SIZE;
+	}
+      else
+	{
+	  int block_size;
+	  if (size * 8 > TRE_MEM_BLOCK_SIZE)
+	    block_size = size * 8;
+	  else
+	    block_size = TRE_MEM_BLOCK_SIZE;
+	  DPRINT(("tre_mem_alloc: allocating new %d byte block\n",
+		  block_size));
+	  l = xmalloc(sizeof(*l));
+	  if (l == NULL)
+	    {
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  l->data = xmalloc(block_size);
+	  if (l->data == NULL)
+	    {
+	      xfree(l);
+	      mem->failed = 1;
+	      return NULL;
+	    }
+	  l->next = NULL;
+	  if (mem->current != NULL)
+	    mem->current->next = l;
+	  if (mem->blocks == NULL)
+	    mem->blocks = l;
+	  mem->current = l;
+	  mem->ptr = l->data;
+	  mem->n = block_size;
+	}
+    }
+
+  /* Make sure the next pointer will be aligned. */
+  size += ALIGN(mem->ptr + size, long);
+
+  /* Allocate from current block. */
+  ptr = mem->ptr;
+  mem->ptr += size;
+  mem->n -= size;
+
+  /* Set to zero if needed. */
+  if (zero)
+    memset(ptr, 0, size);
+
+  return ptr;
+}
+
+/* EOF */
+/*
+  tre-parse.c - Regexp parser
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  This parser is just a simple recursive descent parser for POSIX.2
+  regexps.  The parser supports both the obsolete default syntax and
+  the "extended" syntax, and some nonstandard extensions.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+
+
+/* Characters with special meanings in regexp syntax. */
+#define CHAR_PIPE	   L'|'
+#define CHAR_LPAREN	   L'('
+#define CHAR_RPAREN	   L')'
+#define CHAR_LBRACE	   L'{'
+#define CHAR_RBRACE	   L'}'
+#define CHAR_LBRACKET	   L'['
+#define CHAR_RBRACKET	   L']'
+#define CHAR_MINUS	   L'-'
+#define CHAR_STAR	   L'*'
+#define CHAR_QUESTIONMARK  L'?'
+#define CHAR_PLUS	   L'+'
+#define CHAR_PERIOD	   L'.'
+#define CHAR_COLON	   L':'
+#define CHAR_EQUAL	   L'='
+#define CHAR_COMMA	   L','
+#define CHAR_CARET	   L'^'
+#define CHAR_DOLLAR	   L'$'
+#define CHAR_BACKSLASH	   L'\\'
+#define CHAR_HASH	   L'#'
+#define CHAR_TILDE	   L'~'
+
+
+/* Some macros for expanding \w, \s, etc. */
+static const struct tre_macro_struct {
+  const char c;
+  const char *expansion;
+} tre_macros[] =
+  { {'t', "\t"},	   {'n', "\n"},		   {'r', "\r"},
+    {'f', "\f"},	   {'a', "\a"},		   {'e', "\033"},
+    {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+    {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"},   {'D', "[^[:digit:]]"},
+    { 0, NULL }
+  };
+
+
+/* Expands a macro delimited by `regex' and `regex_end' to `buf', which
+   must have at least `len' items.  Sets buf[0] to zero if the there
+   is no match in `tre_macros'. */
+static void
+tre_expand_macro(const tre_char_t *regex, const tre_char_t *regex_end,
+		 tre_char_t *buf, size_t buf_len)
+{
+  int i;
+
+  buf[0] = 0;
+  if (regex >= regex_end)
+    return;
+
+  for (i = 0; tre_macros[i].expansion; i++)
+    {
+      if (tre_macros[i].c == *regex)
+	{
+	  unsigned int j;
+	  DPRINT(("Expanding macro '%c' => '%s'\n",
+		  tre_macros[i].c, tre_macros[i].expansion));
+	  for (j = 0; tre_macros[i].expansion[j] && j < buf_len; j++)
+	    buf[j] = tre_macros[i].expansion[j];
+	  buf[j] = 0;
+	  break;
+	}
+    }
+}
+
+static reg_errcode_t
+tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i,
+	 tre_ast_node_t ***items)
+{
+  reg_errcode_t status;
+  tre_ast_node_t **array = *items;
+  /* Allocate more space if necessary. */
+  if (*i >= *max_i)
+    {
+      tre_ast_node_t **new_items;
+      DPRINT(("out of array space, i = %d\n", *i));
+      /* If the array is already 1024 items large, give up -- there's
+	 probably an error in the regexp (e.g. not a '\0' terminated
+	 string and missing ']') */
+      if (*max_i > 1024)
+	return REG_ESPACE;
+      *max_i *= 2;
+      new_items = xrealloc(array, sizeof(*items) * *max_i);
+      if (new_items == NULL)
+	return REG_ESPACE;
+      *items = array = new_items;
+    }
+  array[*i] = tre_ast_new_literal(mem, min, max, -1);
+  status = array[*i] == NULL ? REG_ESPACE : REG_OK;
+  (*i)++;
+  return status;
+}
+
+
+/* Expands a character class to character ranges. */
+static reg_errcode_t
+tre_expand_ctype(tre_mem_t mem, tre_ctype_t class, tre_ast_node_t ***items,
+		 int *i, int *max_i, int cflags)
+{
+  reg_errcode_t status = REG_OK;
+  tre_cint_t c;
+  int j, min = -1, max = 0;
+  assert(TRE_MB_CUR_MAX == 1);
+
+  DPRINT(("  expanding class to character ranges\n"));
+  for (j = 0; (j < 256) && (status == REG_OK); j++)
+    {
+      c = j;
+      if (tre_isctype(c, class)
+	  || ((cflags & REG_ICASE)
+	      && (tre_isctype(tre_tolower(c), class)
+		  || tre_isctype(tre_toupper(c), class))))
+{
+	  if (min < 0)
+	    min = c;
+	  max = c;
+	}
+      else if (min >= 0)
+	{
+	  DPRINT(("  range %c (%d) to %c (%d)\n", min, min, max, max));
+	  status = tre_new_item(mem, min, max, i, max_i, items);
+	  min = -1;
+	}
+    }
+  if (min >= 0 && status == REG_OK)
+    status = tre_new_item(mem, min, max, i, max_i, items);
+  return status;
+}
+
+
+static int
+tre_compare_items(const void *a, const void *b)
+{
+  const tre_ast_node_t *node_a = *(tre_ast_node_t * const *)a;
+  const tre_ast_node_t *node_b = *(tre_ast_node_t * const *)b;
+  tre_literal_t *l_a = node_a->obj, *l_b = node_b->obj;
+  int a_min = l_a->code_min, b_min = l_b->code_min;
+
+  if (a_min < b_min)
+    return -1;
+  else if (a_min > b_min)
+    return 1;
+  else
+    return 0;
+}
+
+#ifndef TRE_USE_SYSTEM_WCTYPE
+
+/* isalnum() and the rest may be macros, so wrap them to functions. */
+int tre_isalnum_func(tre_cint_t c) { return tre_isalnum(c); }
+int tre_isalpha_func(tre_cint_t c) { return tre_isalpha(c); }
+
+#ifdef tre_isascii
+int tre_isascii_func(tre_cint_t c) { return tre_isascii(c); }
+#else /* !tre_isascii */
+int tre_isascii_func(tre_cint_t c) { return !(c >> 7); }
+#endif /* !tre_isascii */
+
+#ifdef tre_isblank
+int tre_isblank_func(tre_cint_t c) { return tre_isblank(c); }
+#else /* !tre_isblank */
+int tre_isblank_func(tre_cint_t c) { return ((c == ' ') || (c == '\t')); }
+#endif /* !tre_isblank */
+
+int tre_iscntrl_func(tre_cint_t c) { return tre_iscntrl(c); }
+int tre_isdigit_func(tre_cint_t c) { return tre_isdigit(c); }
+int tre_isgraph_func(tre_cint_t c) { return tre_isgraph(c); }
+int tre_islower_func(tre_cint_t c) { return tre_islower(c); }
+int tre_isprint_func(tre_cint_t c) { return tre_isprint(c); }
+int tre_ispunct_func(tre_cint_t c) { return tre_ispunct(c); }
+int tre_isspace_func(tre_cint_t c) { return tre_isspace(c); }
+int tre_isupper_func(tre_cint_t c) { return tre_isupper(c); }
+int tre_isxdigit_func(tre_cint_t c) { return tre_isxdigit(c); }
+
+struct {
+  char *name;
+  int (*func)(tre_cint_t);
+} tre_ctype_map[] = {
+  { "alnum", &tre_isalnum_func },
+  { "alpha", &tre_isalpha_func },
+#ifdef tre_isascii
+  { "ascii", &tre_isascii_func },
+#endif /* tre_isascii */
+#ifdef tre_isblank
+  { "blank", &tre_isblank_func },
+#endif /* tre_isblank */
+  { "cntrl", &tre_iscntrl_func },
+  { "digit", &tre_isdigit_func },
+  { "graph", &tre_isgraph_func },
+  { "lower", &tre_islower_func },
+  { "print", &tre_isprint_func },
+  { "punct", &tre_ispunct_func },
+  { "space", &tre_isspace_func },
+  { "upper", &tre_isupper_func },
+  { "xdigit", &tre_isxdigit_func },
+  { NULL, NULL}
+};
+
+tre_ctype_t tre_ctype(const char *name)
+{
+  int i;
+  for (i = 0; tre_ctype_map[i].name != NULL; i++)
+    {
+      if (strcmp(name, tre_ctype_map[i].name) == 0)
+	return tre_ctype_map[i].func;
+    }
+  return (tre_ctype_t)0;
+}
+#endif /* !TRE_USE_SYSTEM_WCTYPE */
+
+/* Maximum number of character classes that can occur in a negated bracket
+   expression.	*/
+#define MAX_NEG_CLASSES 64
+
+/* Maximum length of character class names. */
+#define MAX_CLASS_NAME
+
+#define REST(re) (int)(ctx->re_end - (re)), (re)
+
+static reg_errcode_t
+tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
+			tre_ctype_t neg_classes[], int *num_neg_classes,
+			tre_ast_node_t ***items, int *num_items,
+			int *items_size)
+{
+  const tre_char_t *re = ctx->re;
+  reg_errcode_t status = REG_OK;
+  tre_ctype_t class = (tre_ctype_t)0;
+  int i = *num_items;
+  int max_i = *items_size;
+  int skip;
+
+  /* Build an array of the items in the bracket expression. */
+  while (status == REG_OK)
+    {
+      skip = 0;
+      if (re == ctx->re_end)
+	{
+	  status = REG_EBRACK;
+	}
+      else if (*re == CHAR_RBRACKET && re > ctx->re)
+	{
+	  DPRINT(("tre_parse_bracket:	done: '%.*" STRF "'\n", REST(re)));
+	  re++;
+	  break;
+	}
+      else
+	{
+	  tre_cint_t min = 0, max = 0;
+
+	  class = (tre_ctype_t)0;
+	  if (re + 2 < ctx->re_end
+	      && *(re + 1) == CHAR_MINUS && *(re + 2) != CHAR_RBRACKET)
+	    {
+	      DPRINT(("tre_parse_bracket:  range: '%.*" STRF "'\n", REST(re)));
+	      min = *re;
+	      max = *(re + 2);
+	      re += 3;
+	      /* XXX - Should use collation order instead of encoding values
+		 in character ranges. */
+	      if (min > max)
+		status = REG_ERANGE;
+	    }
+	  else if (re + 1 < ctx->re_end
+		   && *re == CHAR_LBRACKET && *(re + 1) == CHAR_PERIOD)
+	    status = REG_ECOLLATE;
+	  else if (re + 1 < ctx->re_end
+		   && *re == CHAR_LBRACKET && *(re + 1) == CHAR_EQUAL)
+	    status = REG_ECOLLATE;
+	  else if (re + 1 < ctx->re_end
+		   && *re == CHAR_LBRACKET && *(re + 1) == CHAR_COLON)
+	    {
+	      char tmp_str[64];
+	      const tre_char_t *endptr = re + 2;
+	      int len;
+	      DPRINT(("tre_parse_bracket:  class: '%.*" STRF "'\n", REST(re)));
+	      while (endptr < ctx->re_end && *endptr != CHAR_COLON)
+		endptr++;
+	      if (endptr != ctx->re_end)
+		{
+		  len = MIN(endptr - re - 2, 63);
+#ifdef TRE_WCHAR
+		  {
+		    tre_char_t tmp_wcs[64];
+		    wcsncpy(tmp_wcs, re + 2, (size_t)len);
+		    tmp_wcs[len] = L'\0';
+#if defined HAVE_WCSRTOMBS
+		    {
+		      mbstate_t state;
+		      const tre_char_t *src = tmp_wcs;
+		      memset(&state, '\0', sizeof(state));
+		      len = wcsrtombs(tmp_str, &src, sizeof(tmp_str), &state);
+		    }
+#elif defined HAVE_WCSTOMBS
+		    len = wcstombs(tmp_str, tmp_wcs, 63);
+#endif /* defined HAVE_WCSTOMBS */
+		  }
+#else /* !TRE_WCHAR */
+		  strncpy(tmp_str, (const char*)re + 2, len);
+#endif /* !TRE_WCHAR */
+		  tmp_str[len] = '\0';
+		  DPRINT(("  class name: %s\n", tmp_str));
+		  class = tre_ctype(tmp_str);
+		  if (!class)
+		    status = REG_ECTYPE;
+		  /* Optimize character classes for 8 bit character sets. */
+		  if (status == REG_OK && TRE_MB_CUR_MAX == 1)
+		    {
+		      status = tre_expand_ctype(ctx->mem, class, items,
+						&i, &max_i, ctx->cflags);
+		      class = (tre_ctype_t)0;
+		      skip = 1;
+		    }
+		  re = endptr + 2;
+		}
+	      else
+		status = REG_ECTYPE;
+	      min = 0;
+	      max = TRE_CHAR_MAX;
+	    }
+	  else
+	    {
+	      DPRINT(("tre_parse_bracket:   char: '%.*" STRF "'\n", REST(re)));
+	      if (*re == CHAR_MINUS && *(re + 1) != CHAR_RBRACKET
+		  && ctx->re != re)
+		/* Two ranges are not allowed to share and endpoint. */
+		status = REG_ERANGE;
+	      min = max = *re++;
+	    }
+
+	  if (status != REG_OK)
+	    break;
+
+	  if (class && negate)
+	    if (*num_neg_classes >= MAX_NEG_CLASSES)
+	      status = REG_ESPACE;
+	    else
+	      neg_classes[(*num_neg_classes)++] = class;
+	  else if (!skip)
+	    {
+	      status = tre_new_item(ctx->mem, min, max, &i, &max_i, items);
+	      if (status != REG_OK)
+		break;
+	      ((tre_literal_t*)((*items)[i-1])->obj)->u.class = class;
+	    }
+
+	  /* Add opposite-case counterpoints if REG_ICASE is present.
+	     This is broken if there are more than two "same" characters. */
+	  if (ctx->cflags & REG_ICASE && !class && status == REG_OK && !skip)
+	    {
+	      tre_cint_t cmin, ccurr;
+
+	      DPRINT(("adding opposite-case counterpoints\n"));
+	      while (min <= max)
+		{
+		  if (tre_islower(min))
+		    {
+		      cmin = ccurr = tre_toupper(min++);
+		      while (tre_islower(min) && tre_toupper(min) == ccurr + 1
+			     && min <= max)
+			ccurr = tre_toupper(min++);
+		      status = tre_new_item(ctx->mem, cmin, ccurr,
+					    &i, &max_i, items);
+		    }
+		  else if (tre_isupper(min))
+		    {
+		      cmin = ccurr = tre_tolower(min++);
+		      while (tre_isupper(min) && tre_tolower(min) == ccurr + 1
+			     && min <= max)
+			ccurr = tre_tolower(min++);
+		      status = tre_new_item(ctx->mem, cmin, ccurr,
+					    &i, &max_i, items);
+		    }
+		  else min++;
+		  if (status != REG_OK)
+		    break;
+		}
+	      if (status != REG_OK)
+		break;
+	    }
+	}
+    }
+  *num_items = i;
+  *items_size = max_i;
+  ctx->re = re;
+  return status;
+}
+
+static reg_errcode_t
+tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
+{
+  tre_ast_node_t *node = NULL;
+  int negate = 0;
+  reg_errcode_t status = REG_OK;
+  tre_ast_node_t **items, *u, *n;
+  int i = 0, j, max_i = 32, curr_max, curr_min;
+  tre_ctype_t neg_classes[MAX_NEG_CLASSES];
+  int num_neg_classes = 0;
+
+  /* Start off with an array of `max_i' elements. */
+  items = xmalloc(sizeof(*items) * max_i);
+  if (items == NULL)
+    return REG_ESPACE;
+
+  if (*ctx->re == CHAR_CARET)
+    {
+      DPRINT(("tre_parse_bracket: negate: '%.*" STRF "'\n", REST(ctx->re)));
+      negate = 1;
+      ctx->re++;
+    }
+
+  status = tre_parse_bracket_items(ctx, negate, neg_classes, &num_neg_classes,
+				   &items, &i, &max_i);
+
+  if (status != REG_OK)
+    goto parse_bracket_done;
+
+  /* Sort the array if we need to negate it. */
+  if (negate)
+    qsort(items, (unsigned)i, sizeof(*items), tre_compare_items);
+
+  curr_max = curr_min = 0;
+  /* Build a union of the items in the array, negated if necessary. */
+  for (j = 0; j < i && status == REG_OK; j++)
+    {
+      int min, max;
+      tre_literal_t *l = items[j]->obj;
+      min = l->code_min;
+      max = l->code_max;
+
+      DPRINT(("item: %d - %d, class %ld, curr_max = %d\n",
+	      (int)l->code_min, (int)l->code_max, (long)l->u.class, curr_max));
+
+      if (negate)
+	{
+	  if (min < curr_max)
+	    {
+	      /* Overlap. */
+	      curr_max = MAX(max + 1, curr_max);
+	      DPRINT(("overlap, curr_max = %d\n", curr_max));
+	      l = NULL;
+	    }
+	  else
+	    {
+	      /* No overlap. */
+	      curr_max = min - 1;
+	      if (curr_max >= curr_min)
+		{
+		  DPRINT(("no overlap\n"));
+		  l->code_min = curr_min;
+		  l->code_max = curr_max;
+		}
+	      else
+		{
+		  DPRINT(("no overlap, zero room\n"));
+		  l = NULL;
+		}
+	      curr_min = curr_max = max + 1;
+	    }
+	}
+
+      if (l != NULL)
+	{
+	  int k;
+	  DPRINT(("creating %d - %d\n", (int)l->code_min, (int)l->code_max));
+	  l->position = ctx->position;
+	  if (num_neg_classes > 0)
+	    {
+	      l->neg_classes = tre_mem_alloc(ctx->mem,
+					     (sizeof(l->neg_classes)
+					      * (num_neg_classes + 1)));
+	      if (l->neg_classes == NULL)
+		{
+		  status = REG_ESPACE;
+		  break;
+		}
+	      for (k = 0; k < num_neg_classes; k++)
+		l->neg_classes[k] = neg_classes[k];
+	      l->neg_classes[k] = (tre_ctype_t)0;
+	    }
+	  else
+	    l->neg_classes = NULL;
+	  if (node == NULL)
+	    node = items[j];
+	  else
+	    {
+	      u = tre_ast_new_union(ctx->mem, node, items[j]);
+	      if (u == NULL)
+		status = REG_ESPACE;
+	      node = u;
+	    }
+	}
+    }
+
+  if (status != REG_OK)
+    goto parse_bracket_done;
+
+  if (negate)
+    {
+      int k;
+      DPRINT(("final: creating %d - %d\n", curr_min, (int)TRE_CHAR_MAX));
+      n = tre_ast_new_literal(ctx->mem, curr_min, TRE_CHAR_MAX, ctx->position);
+      if (n == NULL)
+	status = REG_ESPACE;
+      else
+	{
+	  tre_literal_t *l = n->obj;
+	  if (num_neg_classes > 0)
+	    {
+	      l->neg_classes = tre_mem_alloc(ctx->mem,
+					     (sizeof(l->neg_classes)
+					      * (num_neg_classes + 1)));
+	      if (l->neg_classes == NULL)
+		{
+		  status = REG_ESPACE;
+		  goto parse_bracket_done;
+		}
+	      for (k = 0; k < num_neg_classes; k++)
+		l->neg_classes[k] = neg_classes[k];
+	      l->neg_classes[k] = (tre_ctype_t)0;
+	    }
+	  else
+	    l->neg_classes = NULL;
+	  if (node == NULL)
+	    node = n;
+	  else
+	    {
+	      u = tre_ast_new_union(ctx->mem, node, n);
+	      if (u == NULL)
+		status = REG_ESPACE;
+	      node = u;
+	    }
+	}
+    }
+
+  if (status != REG_OK)
+    goto parse_bracket_done;
+
+#ifdef TRE_DEBUG
+  tre_ast_print(node);
+#endif /* TRE_DEBUG */
+
+ parse_bracket_done:
+  xfree(items);
+  ctx->position++;
+  *result = node;
+  return status;
+}
+
+
+/* Parses a positive decimal integer.  Returns -1 if the string does not
+   contain a valid number. */
+static int
+tre_parse_int(const tre_char_t **regex, const tre_char_t *regex_end)
+{
+  int num = -1;
+  const tre_char_t *r = *regex;
+  while (r < regex_end && *r >= L'0' && *r <= L'9')
+    {
+      if (num < 0)
+	num = 0;
+      num = num * 10 + *r - L'0';
+      r++;
+    }
+  *regex = r;
+  return num;
+}
+
+
+static reg_errcode_t
+tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
+{
+  int min, max, i;
+  int cost_ins, cost_del, cost_subst, cost_max;
+  int limit_ins, limit_del, limit_subst, limit_err;
+  const tre_char_t *r = ctx->re;
+  const tre_char_t *start;
+  int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0;
+  int approx = 0;
+  int costs_set = 0;
+  int counts_set = 0;
+
+  cost_ins = cost_del = cost_subst = cost_max = TRE_PARAM_UNSET;
+  limit_ins = limit_del = limit_subst = limit_err = TRE_PARAM_UNSET;
+
+  /* Parse number (minimum repetition count). */
+  min = -1;
+  if (r < ctx->re_end && *r >= L'0' && *r <= L'9') {
+    DPRINT(("tre_parse:	  min count: '%.*" STRF "'\n", REST(r)));
+    min = tre_parse_int(&r, ctx->re_end);
+  }
+
+  /* Parse comma and second number (maximum repetition count). */
+  max = min;
+  if (r < ctx->re_end && *r == CHAR_COMMA)
+    {
+      r++;
+      DPRINT(("tre_parse:   max count: '%.*" STRF "'\n", REST(r)));
+      max = tre_parse_int(&r, ctx->re_end);
+    }
+
+  /* Check that the repeat counts are sane. */
+  if ((max >= 0 && min > max) || max > RE_DUP_MAX)
+    return REG_BADBR;
+
+
+  /*
+   '{'
+     optionally followed immediately by a number == minimum repcount
+     optionally followed by , then a number == maximum repcount
+      + then a number == maximum insertion count
+      - then a number == maximum deletion count
+      # then a number == maximum substitution count
+      ~ then a number == maximum number of errors
+      Any of +, -, # or ~ without followed by a number means that
+      the maximum count/number of errors is infinite.
+
+      An equation of the form
+	Xi + Yd + Zs < C
+      can be specified to set costs and the cost limit to a value
+      different from the default value:
+	- X is the cost of an insertion
+	- Y is the cost of a deletion
+	- Z is the cost of a substitution
+	- C is the maximum cost
+
+      If no count limit or cost is set for an operation, the operation
+      is not allowed at all.
+  */
+
+
+  do {
+    int done;
+    start = r;
+
+    /* Parse count limit settings */
+    done = 0;
+    if (!counts_set)
+      while (r + 1 < ctx->re_end && !done)
+	{
+	  switch (*r)
+	    {
+	    case CHAR_PLUS:  /* Insert limit */
+	      DPRINT(("tre_parse:   ins limit: '%.*" STRF "'\n", REST(r)));
+	      r++;
+	      limit_ins = tre_parse_int(&r, ctx->re_end);
+	      if (limit_ins < 0)
+		limit_ins = INT_MAX;
+	      counts_set = 1;
+	      break;
+	    case CHAR_MINUS: /* Delete limit */
+	      DPRINT(("tre_parse:   del limit: '%.*" STRF "'\n", REST(r)));
+	      r++;
+	      limit_del = tre_parse_int(&r, ctx->re_end);
+	      if (limit_del < 0)
+		limit_del = INT_MAX;
+	      counts_set = 1;
+	      break;
+	    case CHAR_HASH:  /* Substitute limit */
+	      DPRINT(("tre_parse: subst limit: '%.*" STRF "'\n", REST(r)));
+	      r++;
+	      limit_subst = tre_parse_int(&r, ctx->re_end);
+	      if (limit_subst < 0)
+		limit_subst = INT_MAX;
+	      counts_set = 1;
+	      break;
+	    case CHAR_TILDE: /* Maximum number of changes */
+	      DPRINT(("tre_parse: count limit: '%.*" STRF "'\n", REST(r)));
+	      r++;
+	      limit_err = tre_parse_int(&r, ctx->re_end);
+	      if (limit_err < 0)
+		limit_err = INT_MAX;
+	      approx = 1;
+	      break;
+	    case CHAR_COMMA:
+	      r++;
+	      break;
+	    case L' ':
+	      r++;
+	      break;
+	    case L'}':
+	      done = 1;
+	      break;
+	    default:
+	      done = 1;
+	      break;
+	    }
+	}
+
+    /* Parse cost restriction equation. */
+    done = 0;
+    if (!costs_set)
+      while (r + 1 < ctx->re_end && !done)
+	{
+	  switch (*r)
+	    {
+	    case CHAR_PLUS:
+	    case L' ':
+	      r++;
+	      break;
+	    case L'<':
+	      DPRINT(("tre_parse:    max cost: '%.*" STRF "'\n", REST(r)));
+	      r++;
+	      while (*r == L' ')
+		r++;
+	      cost_max = tre_parse_int(&r, ctx->re_end);
+	      if (cost_max < 0)
+		cost_max = INT_MAX;
+	      else
+		cost_max--;
+	      approx = 1;
+	      break;
+	    case CHAR_COMMA:
+	      r++;
+	      done = 1;
+	      break;
+	    default:
+	      if (*r >= L'0' && *r <= L'9')
+		{
+#ifdef TRE_DEBUG
+		  const tre_char_t *sr = r;
+#endif /* TRE_DEBUG */
+		  int cost = tre_parse_int(&r, ctx->re_end);
+		  /* XXX - make sure r is not past end. */
+		  switch (*r)
+		    {
+		    case L'i':	/* Insert cost */
+		      DPRINT(("tre_parse:    ins cost: '%.*" STRF "'\n",
+			      REST(sr)));
+		      r++;
+		      cost_ins = cost;
+		      costs_set = 1;
+		      break;
+		    case L'd':	/* Delete cost */
+		      DPRINT(("tre_parse:    del cost: '%.*" STRF "'\n",
+			      REST(sr)));
+		      r++;
+		      cost_del = cost;
+		      costs_set = 1;
+		      break;
+		    case L's':	/* Substitute cost */
+		      DPRINT(("tre_parse:  subst cost: '%.*" STRF "'\n",
+			      REST(sr)));
+		      r++;
+		      cost_subst = cost;
+		      costs_set = 1;
+		      break;
+		    default:
+		      return REG_BADBR;
+		    }
+		}
+	      else
+		{
+		  done = 1;
+		  break;
+		}
+	    }
+	}
+  } while (start != r);
+
+  /* Missing }. */
+  if (r >= ctx->re_end)
+    return REG_EBRACE;
+
+  /* Empty contents of {}. */
+  if (r == ctx->re)
+    return REG_BADBR;
+
+  /* Parse the ending '}' or '\}'.*/
+  if (ctx->cflags & REG_EXTENDED)
+    {
+      if (r >= ctx->re_end || *r != CHAR_RBRACE)
+	return REG_BADBR;
+      r++;
+    }
+  else
+    {
+      if (r + 1 >= ctx->re_end
+	  || *r != CHAR_BACKSLASH
+	  || *(r + 1) != CHAR_RBRACE)
+	return REG_BADBR;
+      r += 2;
+    }
+
+
+  /* Parse trailing '?' marking minimal repetition. */
+  if (r < ctx->re_end)
+    {
+      if (*r == CHAR_QUESTIONMARK)
+	{
+	  minimal = !(ctx->cflags & REG_UNGREEDY);
+	  r++;
+	}
+      else if (*r == CHAR_STAR || *r == CHAR_PLUS)
+	{
+	  /* These are reserved for future extensions. */
+	  return REG_BADRPT;
+	}
+    }
+
+  /* Create the AST node(s). */
+  if (min == 0 && max == 0)
+    {
+      *result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+      if (*result == NULL)
+	return REG_ESPACE;
+    }
+  else
+    {
+      if (min < 0 && max < 0)
+	/* Only approximate parameters set, no repetitions. */
+	min = max = 1;
+
+      *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal);
+      if (!*result)
+	return REG_ESPACE;
+
+      /* If approximate matching parameters are set, add them to the
+	 iteration node. */
+      if (approx || costs_set || counts_set)
+	{
+	  int *params;
+	  tre_iteration_t *iter = (*result)->obj;
+
+	  if (costs_set || counts_set)
+	    {
+	      if (limit_ins == TRE_PARAM_UNSET)
+		{
+		  if (cost_ins == TRE_PARAM_UNSET)
+		    limit_ins = 0;
+		  else
+		    limit_ins = INT_MAX;
+		}
+
+	      if (limit_del == TRE_PARAM_UNSET)
+		{
+		  if (cost_del == TRE_PARAM_UNSET)
+		    limit_del = 0;
+		  else
+		    limit_del = INT_MAX;
+		}
+
+	      if (limit_subst == TRE_PARAM_UNSET)
+		{
+		  if (cost_subst == TRE_PARAM_UNSET)
+		    limit_subst = 0;
+		  else
+		    limit_subst = INT_MAX;
+		}
+	    }
+
+	  if (cost_max == TRE_PARAM_UNSET)
+	    cost_max = INT_MAX;
+	  if (limit_err == TRE_PARAM_UNSET)
+	    limit_err = INT_MAX;
+
+	  ctx->have_approx = 1;
+	  params = tre_mem_alloc(ctx->mem, sizeof(*params) * TRE_PARAM_LAST);
+	  if (!params)
+	    return REG_ESPACE;
+	  for (i = 0; i < TRE_PARAM_LAST; i++)
+	    params[i] = TRE_PARAM_UNSET;
+	  params[TRE_PARAM_COST_INS] = cost_ins;
+	  params[TRE_PARAM_COST_DEL] = cost_del;
+	  params[TRE_PARAM_COST_SUBST] = cost_subst;
+	  params[TRE_PARAM_COST_MAX] = cost_max;
+	  params[TRE_PARAM_MAX_INS] = limit_ins;
+	  params[TRE_PARAM_MAX_DEL] = limit_del;
+	  params[TRE_PARAM_MAX_SUBST] = limit_subst;
+	  params[TRE_PARAM_MAX_ERR] = limit_err;
+	  iter->params = params;
+	}
+    }
+
+  DPRINT(("tre_parse_bound: min %d, max %d, costs [%d,%d,%d, total %d], "
+	  "limits [%d,%d,%d, total %d]\n",
+	  min, max, cost_ins, cost_del, cost_subst, cost_max,
+	  limit_ins, limit_del, limit_subst, limit_err));
+
+
+  ctx->re = r;
+  return REG_OK;
+}
+
+typedef enum {
+  PARSE_RE = 0,
+  PARSE_ATOM,
+  PARSE_MARK_FOR_SUBMATCH,
+  PARSE_BRANCH,
+  PARSE_PIECE,
+  PARSE_CATENATION,
+  PARSE_POST_CATENATION,
+  PARSE_UNION,
+  PARSE_POST_UNION,
+  PARSE_POSTFIX,
+  PARSE_RESTORE_CFLAGS
+} tre_parse_re_stack_symbol_t;
+
+
+reg_errcode_t
+tre_parse(tre_parse_ctx_t *ctx)
+{
+  tre_ast_node_t *result = NULL;
+  tre_parse_re_stack_symbol_t symbol;
+  reg_errcode_t status = REG_OK;
+  tre_stack_t *stack = ctx->stack;
+  int bottom = tre_stack_num_objects(stack);
+  int depth = 0;
+  int temporary_cflags = 0;
+
+  DPRINT(("tre_parse: parsing '%.*" STRF "', len = %d\n",
+	  ctx->len, ctx->re, ctx->len));
+
+  if (!ctx->nofirstsub)
+    {
+      STACK_PUSH(stack, int, ctx->submatch_id);
+      STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH);
+      ctx->submatch_id++;
+    }
+  STACK_PUSH(stack, int, PARSE_RE);
+  ctx->re_start = ctx->re;
+  ctx->re_end = ctx->re + ctx->len;
+
+
+  /* The following is basically just a recursive descent parser.  I use
+     an explicit stack instead of recursive functions mostly because of
+     two reasons: compatibility with systems which have an overflowable
+     call stack, and efficiency (both in lines of code and speed).  */
+  while (tre_stack_num_objects(stack) > bottom && status == REG_OK)
+    {
+      if (status != REG_OK)
+	break;
+      symbol = tre_stack_pop_int(stack);
+      switch (symbol)
+	{
+	case PARSE_RE:
+	  /* Parse a full regexp.  A regexp is one or more branches,
+	     separated by the union operator `|'. */
+#ifdef REG_LITERAL
+	  if (!(ctx->cflags & REG_LITERAL)
+	      && ctx->cflags & REG_EXTENDED)
+#endif /* REG_LITERAL */
+	    STACK_PUSHX(stack, int, PARSE_UNION);
+	  STACK_PUSHX(stack, int, PARSE_BRANCH);
+	  break;
+
+	case PARSE_BRANCH:
+	  /* Parse a branch.  A branch is one or more pieces, concatenated.
+	     A piece is an atom possibly followed by a postfix operator. */
+	  STACK_PUSHX(stack, int, PARSE_CATENATION);
+	  STACK_PUSHX(stack, int, PARSE_PIECE);
+	  break;
+
+	case PARSE_PIECE:
+	  /* Parse a piece.  A piece is an atom possibly followed by one
+	     or more postfix operators. */
+#ifdef REG_LITERAL
+	  if (!(ctx->cflags & REG_LITERAL))
+#endif /* REG_LITERAL */
+	    STACK_PUSHX(stack, int, PARSE_POSTFIX);
+	  STACK_PUSHX(stack, int, PARSE_ATOM);
+	  break;
+
+	case PARSE_CATENATION:
+	  /* If the expression has not ended, parse another piece. */
+	  {
+	    tre_char_t c;
+	    if (ctx->re >= ctx->re_end)
+	      break;
+	    c = *ctx->re;
+#ifdef REG_LITERAL
+	    if (!(ctx->cflags & REG_LITERAL))
+	      {
+#endif /* REG_LITERAL */
+		if (ctx->cflags & REG_EXTENDED && c == CHAR_PIPE)
+		  break;
+		if ((ctx->cflags & REG_EXTENDED
+		     && c == CHAR_RPAREN && depth > 0)
+		    || (!(ctx->cflags & REG_EXTENDED)
+			&& (c == CHAR_BACKSLASH
+			    && *(ctx->re + 1) == CHAR_RPAREN)))
+		  {
+		    if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
+		      status = REG_EPAREN;
+		    DPRINT(("tre_parse:	  group end: '%.*" STRF "'\n",
+			    REST(ctx->re)));
+		    depth--;
+		    if (!(ctx->cflags & REG_EXTENDED))
+		      ctx->re += 2;
+		    break;
+		  }
+#ifdef REG_LITERAL
+	      }
+#endif /* REG_LITERAL */
+
+#ifdef REG_RIGHT_ASSOC
+	    if (ctx->cflags & REG_RIGHT_ASSOC)
+	      {
+		/* Right associative concatenation. */
+		STACK_PUSHX(stack, voidptr, result);
+		STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
+		STACK_PUSHX(stack, int, PARSE_CATENATION);
+		STACK_PUSHX(stack, int, PARSE_PIECE);
+	      }
+	    else
+#endif /* REG_RIGHT_ASSOC */
+	      {
+		/* Default case, left associative concatenation. */
+		STACK_PUSHX(stack, int, PARSE_CATENATION);
+		STACK_PUSHX(stack, voidptr, result);
+		STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
+		STACK_PUSHX(stack, int, PARSE_PIECE);
+	      }
+	    break;
+	  }
+
+	case PARSE_POST_CATENATION:
+	  {
+	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
+	    tre_ast_node_t *tmp_node;
+	    tmp_node = tre_ast_new_catenation(ctx->mem, tree, result);
+	    if (!tmp_node)
+	      return REG_ESPACE;
+	    result = tmp_node;
+	    break;
+	  }
+
+	case PARSE_UNION:
+	  if (ctx->re >= ctx->re_end)
+	    break;
+#ifdef REG_LITERAL
+	  if (ctx->cflags & REG_LITERAL)
+	    break;
+#endif /* REG_LITERAL */
+	  switch (*ctx->re)
+	    {
+	    case CHAR_PIPE:
+	      DPRINT(("tre_parse:	union: '%.*" STRF "'\n",
+		      REST(ctx->re)));
+	      STACK_PUSHX(stack, int, PARSE_UNION);
+	      STACK_PUSHX(stack, voidptr, result);
+	      STACK_PUSHX(stack, int, PARSE_POST_UNION);
+	      STACK_PUSHX(stack, int, PARSE_BRANCH);
+	      ctx->re++;
+	      break;
+
+	    case CHAR_RPAREN:
+	      ctx->re++;
+	      break;
+
+	    default:
+	      break;
+	    }
+	  break;
+
+	case PARSE_POST_UNION:
+	  {
+	    tre_ast_node_t *tmp_node;
+	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
+	    tmp_node = tre_ast_new_union(ctx->mem, tree, result);
+	    if (!tmp_node)
+	      return REG_ESPACE;
+	    result = tmp_node;
+	    break;
+	  }
+
+	case PARSE_POSTFIX:
+	  /* Parse postfix operators. */
+	  if (ctx->re >= ctx->re_end)
+	    break;
+#ifdef REG_LITERAL
+	  if (ctx->cflags & REG_LITERAL)
+	    break;
+#endif /* REG_LITERAL */
+	  switch (*ctx->re)
+	    {
+	    case CHAR_PLUS:
+	    case CHAR_QUESTIONMARK:
+	      if (!(ctx->cflags & REG_EXTENDED))
+		break;
+		/*FALLTHROUGH*/
+	    case CHAR_STAR:
+	      {
+		tre_ast_node_t *tmp_node;
+		int minimal = (ctx->cflags & REG_UNGREEDY) ? 1 : 0;
+		int rep_min = 0;
+		int rep_max = -1;
+#ifdef TRE_DEBUG
+		const tre_char_t *tmp_re;
+#endif
+
+		if (*ctx->re == CHAR_PLUS)
+		  rep_min = 1;
+		if (*ctx->re == CHAR_QUESTIONMARK)
+		  rep_max = 1;
+#ifdef TRE_DEBUG
+		tmp_re = ctx->re;
+#endif
+
+		if (ctx->re + 1 < ctx->re_end)
+		  {
+		    if (*(ctx->re + 1) == CHAR_QUESTIONMARK)
+		      {
+			minimal = !(ctx->cflags & REG_UNGREEDY);
+			ctx->re++;
+		      }
+		    else if (*(ctx->re + 1) == CHAR_STAR
+			     || *(ctx->re + 1) == CHAR_PLUS)
+		      {
+			/* These are reserved for future extensions. */
+			return REG_BADRPT;
+		      }
+		  }
+
+		DPRINT(("tre_parse: %s star: '%.*" STRF "'\n",
+			minimal ? "  minimal" : "greedy", REST(tmp_re)));
+		ctx->re++;
+		tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max,
+					    minimal);
+		if (tmp_node == NULL)
+		  return REG_ESPACE;
+		result = tmp_node;
+		STACK_PUSHX(stack, int, PARSE_POSTFIX);
+	      }
+	      break;
+
+	    case CHAR_BACKSLASH:
+	      /* "\{" is special without REG_EXTENDED */
+	      if (!(ctx->cflags & REG_EXTENDED)
+		  && ctx->re + 1 < ctx->re_end
+		  && *(ctx->re + 1) == CHAR_LBRACE)
+		{
+		  ctx->re++;
+		  goto parse_brace;
+		}
+	      else
+		break;
+
+	    case CHAR_LBRACE:
+	      /* "{" is literal without REG_EXTENDED */
+	      if (!(ctx->cflags & REG_EXTENDED))
+		break;
+
+	    parse_brace:
+	      DPRINT(("tre_parse:	bound: '%.*" STRF "'\n",
+		      REST(ctx->re)));
+	      ctx->re++;
+
+	      status = tre_parse_bound(ctx, &result);
+	      if (status != REG_OK)
+		return status;
+	      STACK_PUSHX(stack, int, PARSE_POSTFIX);
+	      break;
+	    }
+	  break;
+
+	case PARSE_ATOM:
+	  /* Parse an atom.  An atom is a regular expression enclosed in `()',
+	     an empty set of `()', a bracket expression, `.', `^', `$',
+	     a `\' followed by a character, or a single character. */
+
+	  /* End of regexp? (empty string). */
+	  if (ctx->re >= ctx->re_end)
+	    goto parse_literal;
+
+#ifdef REG_LITERAL
+	  if (ctx->cflags & REG_LITERAL)
+	    goto parse_literal;
+#endif /* REG_LITERAL */
+
+	  switch (*ctx->re)
+	    {
+	    case CHAR_LPAREN:  /* parenthesized subexpression */
+
+	      /* Handle "(?...)" extensions.  They work in a way similar
+		 to Perls corresponding extensions. */
+	      if (ctx->cflags & REG_EXTENDED
+		  && *(ctx->re + 1) == CHAR_QUESTIONMARK)
+		{
+		  int new_cflags = ctx->cflags;
+		  int bit = 1;
+		  DPRINT(("tre_parse:	extension: '%.*" STRF "\n",
+			  REST(ctx->re)));
+		  ctx->re += 2;
+		  while (/*CONSTCOND*/1)
+		    {
+		      if (*ctx->re == L'i')
+			{
+			  DPRINT(("tre_parse:	    icase: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  if (bit)
+			    new_cflags |= REG_ICASE;
+			  else
+			    new_cflags &= ~REG_ICASE;
+			  ctx->re++;
+			}
+		      else if (*ctx->re == L'n')
+			{
+			  DPRINT(("tre_parse:	  newline: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  if (bit)
+			    new_cflags |= REG_NEWLINE;
+			  else
+			    new_cflags &= ~REG_NEWLINE;
+			  ctx->re++;
+			}
+#ifdef REG_RIGHT_ASSOC
+		      else if (*ctx->re == L'r')
+			{
+			  DPRINT(("tre_parse: right assoc: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  if (bit)
+			    new_cflags |= REG_RIGHT_ASSOC;
+			  else
+			    new_cflags &= ~REG_RIGHT_ASSOC;
+			  ctx->re++;
+			}
+#endif /* REG_RIGHT_ASSOC */
+#ifdef REG_UNGREEDY
+		      else if (*ctx->re == L'U')
+			{
+			  DPRINT(("tre_parse:    ungreedy: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  if (bit)
+			    new_cflags |= REG_UNGREEDY;
+			  else
+			    new_cflags &= ~REG_UNGREEDY;
+			  ctx->re++;
+			}
+#endif /* REG_UNGREEDY */
+		      else if (*ctx->re == CHAR_MINUS)
+			{
+			  DPRINT(("tre_parse:	 turn off: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  ctx->re++;
+			  bit = 0;
+			}
+		      else if (*ctx->re == CHAR_COLON)
+			{
+			  DPRINT(("tre_parse:	 no group: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  ctx->re++;
+			  depth++;
+			  break;
+			}
+		      else if (*ctx->re == CHAR_HASH)
+			{
+			  DPRINT(("tre_parse:    comment: '%.*" STRF "\n",
+				  REST(ctx->re)));
+			  /* A comment can contain any character except a
+			     right parenthesis */
+			  while (*ctx->re != CHAR_RPAREN
+				 && ctx->re < ctx->re_end)
+			    ctx->re++;
+			  if (*ctx->re == CHAR_RPAREN && ctx->re < ctx->re_end)
+			    {
+			      ctx->re++;
+			      break;
+			    }
+			  else
+			    return REG_BADPAT;
+			}
+		      else if (*ctx->re == CHAR_RPAREN)
+			{
+			  ctx->re++;
+			  break;
+			}
+		      else
+			return REG_BADPAT;
+		    }
+
+		  /* Turn on the cflags changes for the rest of the
+		     enclosing group. */
+		  STACK_PUSHX(stack, int, ctx->cflags);
+		  STACK_PUSHX(stack, int, PARSE_RESTORE_CFLAGS);
+		  STACK_PUSHX(stack, int, PARSE_RE);
+		  ctx->cflags = new_cflags;
+		  break;
+		}
+
+	      if (ctx->cflags & REG_EXTENDED
+		  || (ctx->re > ctx->re_start
+		      && *(ctx->re - 1) == CHAR_BACKSLASH))
+		{
+		  depth++;
+		  if (ctx->re + 2 < ctx->re_end
+		      && *(ctx->re + 1) == CHAR_QUESTIONMARK
+		      && *(ctx->re + 2) == CHAR_COLON)
+		    {
+		      DPRINT(("tre_parse: group begin: '%.*" STRF
+			      "', no submatch\n", REST(ctx->re)));
+		      /* Don't mark for submatching. */
+		      ctx->re += 3;
+		      STACK_PUSHX(stack, int, PARSE_RE);
+		    }
+		  else
+		    {
+		      DPRINT(("tre_parse: group begin: '%.*" STRF
+			      "', submatch %d\n", REST(ctx->re),
+			      ctx->submatch_id));
+		      ctx->re++;
+		      /* First parse a whole RE, then mark the resulting tree
+			 for submatching. */
+		      STACK_PUSHX(stack, int, ctx->submatch_id);
+		      STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
+		      STACK_PUSHX(stack, int, PARSE_RE);
+		      ctx->submatch_id++;
+		    }
+		}
+	      else
+		goto parse_literal;
+	      break;
+
+	    case CHAR_RPAREN:  /* end of current subexpression */
+	      if ((ctx->cflags & REG_EXTENDED && depth > 0)
+		  || (ctx->re > ctx->re_start
+		      && *(ctx->re - 1) == CHAR_BACKSLASH))
+		{
+		  DPRINT(("tre_parse:	    empty: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  /* We were expecting an atom, but instead the current
+		     subexpression was closed.	POSIX leaves the meaning of
+		     this to be implementation-defined.	 We interpret this as
+		     an empty expression (which matches an empty string).  */
+		  result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+		  if (result == NULL)
+		    return REG_ESPACE;
+		  if (!(ctx->cflags & REG_EXTENDED))
+		    ctx->re--;
+		}
+	      else
+		goto parse_literal;
+	      break;
+
+	    case CHAR_LBRACKET: /* bracket expression */
+	      DPRINT(("tre_parse:     bracket: '%.*" STRF "'\n",
+		      REST(ctx->re)));
+	      ctx->re++;
+	      status = tre_parse_bracket(ctx, &result);
+	      if (status != REG_OK)
+		return status;
+	      break;
+
+	    case CHAR_BACKSLASH:
+	      /* If this is "\(" or "\)" chew off the backslash and
+		 try again. */
+	      if (!(ctx->cflags & REG_EXTENDED)
+		  && ctx->re + 1 < ctx->re_end
+		  && (*(ctx->re + 1) == CHAR_LPAREN
+		      || *(ctx->re + 1) == CHAR_RPAREN))
+		{
+		  ctx->re++;
+		  STACK_PUSHX(stack, int, PARSE_ATOM);
+		  break;
+		}
+
+	      /* If a macro is used, parse the expanded macro recursively. */
+	      {
+		tre_char_t buf[64];
+		tre_expand_macro(ctx->re + 1, ctx->re_end,
+				 buf, elementsof(buf));
+		if (buf[0] != 0)
+		  {
+		    tre_parse_ctx_t subctx;
+		    memcpy(&subctx, ctx, sizeof(subctx));
+		    subctx.re = buf;
+		    subctx.len = tre_strlen(buf);
+		    subctx.nofirstsub = 1;
+		    status = tre_parse(&subctx);
+		    if (status != REG_OK)
+		      return status;
+		    ctx->re += 2;
+		    ctx->position = subctx.position;
+		    result = subctx.result;
+		    break;
+		  }
+	      }
+
+	      if (ctx->re + 1 >= ctx->re_end)
+		/* Trailing backslash. */
+		return REG_EESCAPE;
+
+#ifdef REG_LITERAL
+	      if (*(ctx->re + 1) == L'Q')
+		{
+		  DPRINT(("tre_parse: tmp literal: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  ctx->cflags |= REG_LITERAL;
+		  temporary_cflags |= REG_LITERAL;
+		  ctx->re += 2;
+		  STACK_PUSHX(stack, int, PARSE_ATOM);
+		  break;
+		}
+#endif /* REG_LITERAL */
+
+	      DPRINT(("tre_parse:  bleep: '%.*" STRF "'\n", REST(ctx->re)));
+	      ctx->re++;
+	      switch (*ctx->re)
+		{
+		case L'b':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_WB, -1);
+		  ctx->re++;
+		  break;
+		case L'B':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_WB_NEG, -1);
+		  ctx->re++;
+		  break;
+		case L'<':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_BOW, -1);
+		  ctx->re++;
+		  break;
+		case L'>':
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_EOW, -1);
+		  ctx->re++;
+		  break;
+		case L'x':
+		  ctx->re++;
+		  if (ctx->re[0] != CHAR_LBRACE && ctx->re < ctx->re_end)
+		    {
+		      /* 8 bit hex char. */
+		      char tmp[3] = {0, 0, 0};
+		      long val;
+		      DPRINT(("tre_parse:  8 bit hex: '%.*" STRF "'\n",
+			      REST(ctx->re - 2)));
+
+		      if (tre_isxdigit(ctx->re[0]) && ctx->re < ctx->re_end)
+			{
+			  tmp[0] = (char)ctx->re[0];
+			  ctx->re++;
+			}
+		      if (tre_isxdigit(ctx->re[0]) && ctx->re < ctx->re_end)
+			{
+			  tmp[1] = (char)ctx->re[0];
+			  ctx->re++;
+			}
+		      val = strtol(tmp, NULL, 16);
+		      result = tre_ast_new_literal(ctx->mem, (int)val,
+						   (int)val, ctx->position);
+		      ctx->position++;
+		      break;
+		    }
+		  else if (ctx->re < ctx->re_end)
+		    {
+		      /* Wide char. */
+		      char tmp[32];
+		      long val;
+		      int i = 0;
+		      ctx->re++;
+		      while (ctx->re_end - ctx->re >= 0)
+			{
+			  if (ctx->re[0] == CHAR_RBRACE)
+			    break;
+			  if (tre_isxdigit(ctx->re[0]))
+			    {
+			      tmp[i] = (char)ctx->re[0];
+			      i++;
+			      ctx->re++;
+			      continue;
+			    }
+			  return REG_EBRACE;
+			}
+		      ctx->re++;
+		      tmp[i] = 0;
+		      val = strtol(tmp, NULL, 16);
+		      result = tre_ast_new_literal(ctx->mem, (int)val, (int)val,
+						   ctx->position);
+		      ctx->position++;
+		      break;
+		    }
+		  /*FALLTHROUGH*/
+
+		default:
+		  if (tre_isdigit(*ctx->re))
+		    {
+		      /* Back reference. */
+		      int val = *ctx->re - L'0';
+		      DPRINT(("tre_parse:     backref: '%.*" STRF "'\n",
+			      REST(ctx->re - 1)));
+		      result = tre_ast_new_literal(ctx->mem, BACKREF, val,
+						   ctx->position);
+		      if (result == NULL)
+			return REG_ESPACE;
+		      ctx->position++;
+		      ctx->max_backref = MAX(val, ctx->max_backref);
+		      ctx->re++;
+		    }
+		  else
+		    {
+		      /* Escaped character. */
+		      DPRINT(("tre_parse:     escaped: '%.*" STRF "'\n",
+			      REST(ctx->re - 1)));
+		      result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
+						   ctx->position);
+		      ctx->position++;
+		      ctx->re++;
+		    }
+		  break;
+		}
+	      if (result == NULL)
+		return REG_ESPACE;
+	      break;
+
+	    case CHAR_PERIOD:	 /* the any-symbol */
+	      DPRINT(("tre_parse:	  any: '%.*" STRF "'\n",
+		      REST(ctx->re)));
+	      if (ctx->cflags & REG_NEWLINE)
+		{
+		  tre_ast_node_t *tmp1;
+		  tre_ast_node_t *tmp2;
+		  tmp1 = tre_ast_new_literal(ctx->mem, 0, L'\n' - 1,
+					     ctx->position);
+		  if (!tmp1)
+		    return REG_ESPACE;
+		  tmp2 = tre_ast_new_literal(ctx->mem, L'\n' + 1, TRE_CHAR_MAX,
+					     ctx->position + 1);
+		  if (!tmp2)
+		    return REG_ESPACE;
+		  result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+		  if (!result)
+		    return REG_ESPACE;
+		  ctx->position += 2;
+		}
+	      else
+		{
+		  result = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX,
+					       ctx->position);
+		  if (!result)
+		    return REG_ESPACE;
+		  ctx->position++;
+		}
+	      ctx->re++;
+	      break;
+
+	    case CHAR_CARET:	 /* beginning of line assertion */
+	      /* '^' has a special meaning everywhere in EREs, and in the
+		 beginning of the RE and after \( is BREs. */
+	      if (ctx->cflags & REG_EXTENDED
+		  || (ctx->re - 2 >= ctx->re_start
+		      && *(ctx->re - 2) == CHAR_BACKSLASH
+		      && *(ctx->re - 1) == CHAR_LPAREN)
+		  || ctx->re == ctx->re_start)
+		{
+		  DPRINT(("tre_parse:	      BOL: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_BOL, -1);
+		  if (result == NULL)
+		    return REG_ESPACE;
+		  ctx->re++;
+		}
+	      else
+		goto parse_literal;
+	      break;
+
+	    case CHAR_DOLLAR:	 /* end of line assertion. */
+	      /* '$' is special everywhere in EREs, and in the end of the
+		 string and before \) is BREs. */
+	      if (ctx->cflags & REG_EXTENDED
+		  || (ctx->re + 2 < ctx->re_end
+		      && *(ctx->re + 1) == CHAR_BACKSLASH
+		      && *(ctx->re + 2) == CHAR_RPAREN)
+		  || ctx->re + 1 == ctx->re_end)
+		{
+		  DPRINT(("tre_parse:	      EOL: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
+					       ASSERT_AT_EOL, -1);
+		  if (result == NULL)
+		    return REG_ESPACE;
+		  ctx->re++;
+		}
+	      else
+		goto parse_literal;
+	      break;
+
+	    default:
+	    parse_literal:
+
+	      if (temporary_cflags && ctx->re + 1 < ctx->re_end
+		  && *ctx->re == CHAR_BACKSLASH && *(ctx->re + 1) == L'E')
+		{
+		  DPRINT(("tre_parse:	 end tmps: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  ctx->cflags &= ~temporary_cflags;
+		  temporary_cflags = 0;
+		  ctx->re += 2;
+		  STACK_PUSHX(stack, int, PARSE_PIECE);
+		  break;
+		}
+
+
+	      /* We are expecting an atom.  If the subexpression (or the whole
+		 regexp ends here, we interpret it as an empty expression
+		 (which matches an empty string).  */
+	      if (
+#ifdef REG_LITERAL
+		  !(ctx->cflags & REG_LITERAL) &&
+#endif /* REG_LITERAL */
+		  (ctx->re >= ctx->re_end
+		   || *ctx->re == CHAR_STAR
+		   || (ctx->cflags & REG_EXTENDED
+		       && (*ctx->re == CHAR_PIPE
+			   || *ctx->re == CHAR_LBRACE
+			   || *ctx->re == CHAR_PLUS
+			   || *ctx->re == CHAR_QUESTIONMARK))
+		   /* Test for "\)" in BRE mode. */
+		   || (!(ctx->cflags & REG_EXTENDED)
+		       && ctx->re + 1 < ctx->re_end
+		       && *ctx->re == CHAR_BACKSLASH
+		       && *(ctx->re + 1) == CHAR_LBRACE)))
+		{
+		  DPRINT(("tre_parse:	    empty: '%.*" STRF "'\n",
+			  REST(ctx->re)));
+		  result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+		  if (!result)
+		    return REG_ESPACE;
+		  break;
+		}
+
+	      DPRINT(("tre_parse:     literal: '%.*" STRF "'\n",
+		      REST(ctx->re)));
+	      /* Note that we can't use an tre_isalpha() test here, since there
+		 may be characters which are alphabetic but neither upper or
+		 lower case. */
+	      if (ctx->cflags & REG_ICASE
+		  && (tre_isupper(*ctx->re) || tre_islower(*ctx->re)))
+		{
+		  tre_ast_node_t *tmp1;
+		  tre_ast_node_t *tmp2;
+
+		  /* XXX - Can there be more than one opposite-case
+		     counterpoints for some character in some locale?  Or
+		     more than two characters which all should be regarded
+		     the same character if case is ignored?  If yes, there
+		     does not seem to be a portable way to detect it.  I guess
+		     that at least for multi-character collating elements there
+		     could be several opposite-case counterpoints, but they
+		     cannot be supported portably anyway. */
+		  tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(*ctx->re),
+					     tre_toupper(*ctx->re),
+					     ctx->position);
+		  if (!tmp1)
+		    return REG_ESPACE;
+		  tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(*ctx->re),
+					     tre_tolower(*ctx->re),
+					     ctx->position);
+		  if (!tmp2)
+		    return REG_ESPACE;
+		  result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+		  if (!result)
+		    return REG_ESPACE;
+		}
+	      else
+		{
+		  result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re,
+					       ctx->position);
+		  if (!result)
+		    return REG_ESPACE;
+		}
+	      ctx->position++;
+	      ctx->re++;
+	      break;
+	    }
+	  break;
+
+	case PARSE_MARK_FOR_SUBMATCH:
+	  {
+	    int submatch_id = tre_stack_pop_int(stack);
+
+	    if (result->submatch_id >= 0)
+	      {
+		tre_ast_node_t *n, *tmp_node;
+		n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+		if (n == NULL)
+		  return REG_ESPACE;
+		tmp_node = tre_ast_new_catenation(ctx->mem, n, result);
+		if (tmp_node == NULL)
+		  return REG_ESPACE;
+		tmp_node->num_submatches = result->num_submatches;
+		result = tmp_node;
+	      }
+	    result->submatch_id = submatch_id;
+	    result->num_submatches++;
+	    break;
+	  }
+
+	case PARSE_RESTORE_CFLAGS:
+	  ctx->cflags = tre_stack_pop_int(stack);
+	  break;
+
+	default:
+	  assert(0);
+	  break;
+	}
+    }
+
+  /* Check for missing closing parentheses. */
+  if (depth > 0)
+    return REG_EPAREN;
+
+  if (status == REG_OK)
+    ctx->result = result;
+
+  return status;
+}
+
+/* EOF */
+/*
+  tre-stack.c - Simple stack implementation
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <stdlib.h>
+#include <assert.h>
+
+
+union tre_stack_item {
+  void *voidptr_value;
+  int int_value;
+};
+
+struct tre_stack_rec {
+  int size;
+  int max_size;
+  int increment;
+  int ptr;
+  union tre_stack_item *stack;
+};
+
+
+tre_stack_t *
+tre_stack_new(int size, int max_size, int increment)
+{
+  tre_stack_t *s;
+
+  s = xmalloc(sizeof(*s));
+  if (s != NULL)
+    {
+      s->stack = xmalloc(sizeof(*s->stack) * size);
+      if (s->stack == NULL)
+	{
+	  xfree(s);
+	  return NULL;
+	}
+      s->size = size;
+      s->max_size = max_size;
+      s->increment = increment;
+      s->ptr = 0;
+    }
+  return s;
+}
+
+void
+tre_stack_destroy(tre_stack_t *s)
+{
+  xfree(s->stack);
+  xfree(s);
+}
+
+int
+tre_stack_num_objects(tre_stack_t *s)
+{
+  return s->ptr;
+}
+
+static reg_errcode_t
+tre_stack_push(tre_stack_t *s, union tre_stack_item value)
+{
+  if (s->ptr < s->size)
+    {
+      s->stack[s->ptr] = value;
+      s->ptr++;
+    }
+  else
+    {
+      if (s->size >= s->max_size)
+	{
+	  DPRINT(("tre_stack_push: stack full\n"));
+	  return REG_ESPACE;
+	}
+      else
+	{
+	  union tre_stack_item *new_buffer;
+	  int new_size;
+	  DPRINT(("tre_stack_push: trying to realloc more space\n"));
+	  new_size = s->size + s->increment;
+	  if (new_size > s->max_size)
+	    new_size = s->max_size;
+	  new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size);
+	  if (new_buffer == NULL)
+	    {
+	      DPRINT(("tre_stack_push: realloc failed.\n"));
+	      return REG_ESPACE;
+	    }
+	  DPRINT(("tre_stack_push: realloc succeeded.\n"));
+	  assert(new_size > s->size);
+	  s->size = new_size;
+	  s->stack = new_buffer;
+	  tre_stack_push(s, value);
+	}
+    }
+  return REG_OK;
+}
+
+#define define_pushf(typetag, type)  \
+  declare_pushf(typetag, type) {     \
+    union tre_stack_item item;	     \
+    item.typetag ## _value = value;  \
+    return tre_stack_push(s, item);  \
+}
+
+define_pushf(int, int)
+define_pushf(voidptr, void *)
+
+#define define_popf(typetag, type)		    \
+  declare_popf(typetag, type) {			    \
+    return s->stack[--s->ptr].typetag ## _value;    \
+  }
+
+define_popf(int, int)
+define_popf(voidptr, void *)
+
+/* EOF */
+/*
+  xmalloc.c - Simple malloc debugging library implementation
+
+  This software is released under a BSD-style license.
+  See the file LICENSE for details and copyright.
+
+*/
+
+/*
+  TODO:
+   - red zones
+   - group dumps by source location
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#define XMALLOC_INTERNAL 1
+
+
+/*
+  Internal stuff.
+*/
+
+typedef struct hashTableItemRec {
+  void *ptr;
+  int bytes;
+  const char *file;
+  int line;
+  const char *func;
+  struct hashTableItemRec *next;
+} hashTableItem;
+
+typedef struct {
+  hashTableItem **table;
+} hashTable;
+
+static int xmalloc_peak;
+int xmalloc_current;
+static int xmalloc_peak_blocks;
+int xmalloc_current_blocks;
+static int xmalloc_fail_after;
+
+#define TABLE_BITS 8
+#define TABLE_MASK ((1 << TABLE_BITS) - 1)
+#define TABLE_SIZE (1 << TABLE_BITS)
+
+static hashTable *
+hash_table_new(void)
+{
+  hashTable *tbl;
+
+  tbl = malloc(sizeof(*tbl));
+
+  if (tbl != NULL)
+    {
+      tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table));
+
+      if (tbl->table == NULL)
+	{
+	  free(tbl);
+	  return NULL;
+	}
+    }
+
+  return tbl;
+}
+
+static int
+hash_void_ptr(void *ptr)
+{
+  int hash;
+  int i;
+
+  /* I took this hash function just off the top of my head, I have
+     no idea whether it is bad or very bad. */
+  hash = 0;
+  for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++)
+    {
+      hash ^= (unsigned long)ptr >> i*8;
+      hash += i * 17;
+      hash &= TABLE_MASK;
+    }
+  return hash;
+}
+
+static void
+hash_table_add(hashTable *tbl, void *ptr, int bytes,
+	       const char *file, int line, const char *func)
+{
+  int i;
+  hashTableItem *item, *new;
+
+  i = hash_void_ptr(ptr);
+
+  item = tbl->table[i];
+  if (item != NULL)
+    while (item->next != NULL)
+      item = item->next;
+
+  new = malloc(sizeof(*new));
+  assert(new != NULL);
+  new->ptr = ptr;
+  new->bytes = bytes;
+  new->file = file;
+  new->line = line;
+  new->func = func;
+  new->next = NULL;
+  if (item != NULL)
+    item->next = new;
+  else
+    tbl->table[i] = new;
+
+  xmalloc_current += bytes;
+  if (xmalloc_current > xmalloc_peak)
+    xmalloc_peak = xmalloc_current;
+  xmalloc_current_blocks++;
+  if (xmalloc_current_blocks > xmalloc_peak_blocks)
+    xmalloc_peak_blocks = xmalloc_current_blocks;
+}
+
+static void
+hash_table_del(hashTable *tbl, void *ptr)
+{
+  int i;
+  hashTableItem *item, *prev;
+
+  i = hash_void_ptr(ptr);
+
+  item = tbl->table[i];
+  if (item == NULL)
+    {
+      printf("xfree: invalid ptr %p\n", ptr);
+      abort();
+    }
+  prev = NULL;
+  while (item->ptr != ptr)
+    {
+      prev = item;
+      item = item->next;
+    }
+  if (item->ptr != ptr)
+    {
+      printf("xfree: invalid ptr %p\n", ptr);
+      abort();
+    }
+
+  xmalloc_current -= item->bytes;
+  xmalloc_current_blocks--;
+
+  if (prev != NULL)
+    {
+      prev->next = item->next;
+      free(item);
+    }
+  else
+    {
+      tbl->table[i] = item->next;
+      free(item);
+    }
+}
+
+static hashTable *xmalloc_table = NULL;
+
+static void
+xmalloc_init(void)
+{
+  if (xmalloc_table == NULL)
+    {
+      xmalloc_table = hash_table_new();
+      xmalloc_peak = 0;
+      xmalloc_peak_blocks = 0;
+      xmalloc_current = 0;
+      xmalloc_current_blocks = 0;
+      xmalloc_fail_after = -1;
+    }
+  assert(xmalloc_table != NULL);
+  assert(xmalloc_table->table != NULL);
+}
+
+
+
+/*
+  Public API.
+*/
+
+void
+xmalloc_configure(int fail_after)
+{
+  xmalloc_init();
+  xmalloc_fail_after = fail_after;
+}
+
+int
+xmalloc_dump_leaks(void)
+{
+  int i;
+  int num_leaks = 0;
+  int leaked_bytes = 0;
+  hashTableItem *item;
+
+  xmalloc_init();
+
+  for (i = 0; i < TABLE_SIZE; i++)
+    {
+      item = xmalloc_table->table[i];
+      while (item != NULL)
+	{
+	  printf("%s:%d: %s: %d bytes at %p not freed\n",
+		 item->file, item->line, item->func, item->bytes, item->ptr);
+	  num_leaks++;
+	  leaked_bytes += item->bytes;
+	  item = item->next;
+	}
+    }
+  if (num_leaks == 0)
+    printf("No memory leaks.\n");
+  else
+    printf("%d unfreed memory chuncks, total %d unfreed bytes.\n",
+	   num_leaks, leaked_bytes);
+  printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ",
+	 xmalloc_peak, (double)xmalloc_peak / 1024,
+	 (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks);
+  printf("(average ");
+  if (xmalloc_peak_blocks)
+    printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2)
+		  / xmalloc_peak_blocks));
+  else
+    printf("N/A");
+  printf(" bytes per block).\n");
+
+  return num_leaks;
+}
+
+void *
+xmalloc_impl(size_t size, const char *file, int line, const char *func)
+{
+  void *ptr;
+
+  xmalloc_init();
+  assert(size > 0);
+
+  if (xmalloc_fail_after == 0)
+    {
+      xmalloc_fail_after = -2;
+#if 0
+      printf("xmalloc: forced failure %s:%d: %s\n", file, line, func);
+#endif
+      return NULL;
+    }
+  else if (xmalloc_fail_after == -2)
+    {
+      printf("xmalloc: called after failure from %s:%d: %s\n",
+	     file, line, func);
+      assert(0);
+    }
+  else if (xmalloc_fail_after > 0)
+    xmalloc_fail_after--;
+
+  ptr = malloc(size);
+  if (ptr != NULL)
+    hash_table_add(xmalloc_table, ptr, (int)size, file, line, func);
+  return ptr;
+}
+
+void *
+xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
+	     const char *func)
+{
+  void *ptr;
+
+  xmalloc_init();
+  assert(size > 0);
+
+  if (xmalloc_fail_after == 0)
+    {
+      xmalloc_fail_after = -2;
+#if 0
+      printf("xcalloc: forced failure %s:%d: %s\n", file, line, func);
+#endif
+      return NULL;
+    }
+  else if (xmalloc_fail_after == -2)
+    {
+      printf("xcalloc: called after failure from %s:%d: %s\n",
+	     file, line, func);
+      assert(0);
+    }
+  else if (xmalloc_fail_after > 0)
+    xmalloc_fail_after--;
+
+  ptr = calloc(nmemb, size);
+  if (ptr != NULL)
+    hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func);
+  return ptr;
+}
+
+void
+xfree_impl(void *ptr, const char *file, int line, const char *func)
+{
+  /*LINTED*/(void)&file;
+  /*LINTED*/(void)&line;
+  /*LINTED*/(void)&func;
+  xmalloc_init();
+
+  if (ptr != NULL)
+    hash_table_del(xmalloc_table, ptr);
+  free(ptr);
+}
+
+void *
+xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
+	      const char *func)
+{
+  void *new_ptr;
+
+  xmalloc_init();
+  assert(ptr != NULL);
+  assert(new_size > 0);
+
+  if (xmalloc_fail_after == 0)
+    {
+      xmalloc_fail_after = -2;
+      return NULL;
+    }
+  else if (xmalloc_fail_after == -2)
+    {
+      printf("xrealloc: called after failure from %s:%d: %s\n",
+	     file, line, func);
+      assert(0);
+    }
+  else if (xmalloc_fail_after > 0)
+    xmalloc_fail_after--;
+
+  new_ptr = realloc(ptr, new_size);
+  if (new_ptr != NULL)
+    {
+      hash_table_del(xmalloc_table, ptr);
+      hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func);
+    }
+  return new_ptr;
+}
+
+
+
+/* EOF */
diff --git a/lib/wrappers/tre/version.txt b/lib/wrappers/tre/version.txt
new file mode 100644
index 000000000..4d01880a7
--- /dev/null
+++ b/lib/wrappers/tre/version.txt
@@ -0,0 +1 @@
+0.7.6
\ No newline at end of file
diff --git a/nim/ast.pas b/nim/ast.pas
index aec165585..cbe5b37fd 100755
--- a/nim/ast.pas
+++ b/nim/ast.pas
@@ -84,20 +84,21 @@ type
     nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64, 
     nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray, 
     nkAsgn, nkFastAsgn, nkGenericParams, nkFormalParams, 
-    nkOfInherit, nkModule, nkProcDef, 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);
+    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 = (
@@ -118,20 +119,21 @@ const
     'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 
     'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 
     'nkAsgn', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 
-    'nkOfInherit', 'nkModule', 'nkProcDef', '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');
+    '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, 
@@ -200,17 +202,19 @@ type
   TSymKind = (
     skUnknown, skConditional, skDynLib, skParam, 
     skGenericParam, skTemp, skType, skConst, 
-    skVar, skProc, skIterator, skConverter, 
-    skMacro, skTemplate, skField, skEnumField, 
-    skForVar, skModule, skLabel, skStub);
+    skVar, skProc, skMethod, skIterator, 
+    skConverter, skMacro, skTemplate, skField, 
+    skEnumField, skForVar, skModule, skLabel, 
+    skStub);
   TSymKinds = set of TSymKind;
 const
   SymKindToStr: array [TSymKind] of string = (
     'skUnknown', 'skConditional', 'skDynLib', 'skParam', 
     'skGenericParam', 'skTemp', 'skType', 'skConst', 
-    'skVar', 'skProc', 'skIterator', 'skConverter', 
-    'skMacro', 'skTemplate', 'skField', 'skEnumField', 
-    'skForVar', 'skModule', 'skLabel', 'skStub');
+    'skVar', 'skProc', 'skMethod', 'skIterator', 
+    'skConverter', 'skMacro', 'skTemplate', 'skField', 
+    'skEnumField', 'skForVar', 'skModule', 'skLabel', 
+    'skStub');
 {[[[end]]]}
 
 type
@@ -223,9 +227,9 @@ type
     //  if (i+1) % 6 == 0: cog.outl("")
     //cog.outl("m" + magics[-1])
     //]]]
-    mNone, mDefined, mLow, mHigh, mSizeOf, mIs, 
-    mEcho, mSucc, mPred, mInc, mDec, mOrd, 
-    mNew, mNewFinalize, mNewSeq, mRegisterFinalizer, mLengthOpenArray, mLengthStr, 
+    mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, 
+    mIs, mEcho, mSucc, mPred, mInc, mDec, 
+    mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mLengthStr, 
     mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, 
     mGCref, mGCunref, mAddI, mSubI, mMulI, mDivI, 
     mModI, mAddI64, mSubI64, mMulI64, mDivI64, mModI64, 
@@ -247,21 +251,20 @@ type
     mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, 
     mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, 
     mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, 
-    mConTT, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, 
-    mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, 
-    mAssert, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, 
-    mNewString, mArray, mOpenArray, mRange, mSet, mSeq, 
-    mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, 
-    mFloat, mFloat32, mFloat64, mBool, mChar, mString, 
-    mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, 
-    mStmt, mTypeDesc, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, 
-    mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, 
-    mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild, 
-    mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal, 
-    mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, 
-    mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode, 
-    mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning, 
-    mNError
+    mConTT, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInRange, 
+    mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert, 
+    mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, 
+    mArray, mOpenArray, mRange, mSet, mSeq, mOrdinal, 
+    mInt, mInt8, mInt16, mInt32, mInt64, mFloat, 
+    mFloat32, mFloat64, mBool, mChar, mString, mCstring, 
+    mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, 
+    mTypeDesc, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, 
+    mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mNaN, 
+    mInf, mNegInf, mNLen, mNChild, mNSetChild, mNAdd, 
+    mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal, mNSymbol, 
+    mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, 
+    mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, 
+    mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning, mNError
     //[[[end]]]
   );
 
@@ -472,7 +475,7 @@ type
   end;
 
 const
-  OverloadableSyms = {@set}[skProc, skIterator, skConverter];
+  OverloadableSyms = {@set}[skProc, skMethod, skIterator, skConverter];
 
 const // "MagicToStr" array:
   MagicToStr: array [TMagic] of string = (
@@ -482,9 +485,9 @@ const // "MagicToStr" array:
     //  if (i+1) % 6 == 0: cog.outl("")
     //cog.outl("'%s'" % magics[-1])
     //]]]
-    'None', 'Defined', 'Low', 'High', 'SizeOf', 'Is', 
-    'Echo', 'Succ', 'Pred', 'Inc', 'Dec', 'Ord', 
-    'New', 'NewFinalize', 'NewSeq', 'RegisterFinalizer', 'LengthOpenArray', 'LengthStr', 
+    'None', 'Defined', 'DefinedInScope', 'Low', 'High', 'SizeOf', 
+    'Is', 'Echo', 'Succ', 'Pred', 'Inc', 'Dec', 
+    'Ord', 'New', 'NewFinalize', 'NewSeq', 'LengthOpenArray', 'LengthStr', 
     'LengthArray', 'LengthSeq', 'Incl', 'Excl', 'Card', 'Chr', 
     'GCref', 'GCunref', 'AddI', 'SubI', 'MulI', 'DivI', 
     'ModI', 'AddI64', 'SubI64', 'MulI64', 'DivI64', 'ModI64', 
@@ -506,21 +509,20 @@ const // "MagicToStr" array:
     'StrToStr', 'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr', 
     'LtStr', 'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet', 
     'MinusSet', 'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 
-    'ConTT', 'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 
-    'InRange', 'InSet', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 
-    'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'CopyStr', 'CopyStrLast', 
-    'NewString', 'Array', 'OpenArray', 'Range', 'Set', 'Seq', 
-    'Ordinal', 'Int', 'Int8', 'Int16', 'Int32', 'Int64', 
-    'Float', 'Float32', 'Float64', 'Bool', 'Char', 'String', 
-    'Cstring', 'Pointer', 'EmptySet', 'IntSetBaseType', 'Nil', 'Expr', 
-    'Stmt', 'TypeDesc', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 
-    'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU', 
-    'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild', 
-    'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal', 
-    'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal', 
-    'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 
-    'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning', 
-    'NError'
+    'ConTT', 'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'InRange', 
+    'InSet', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'Assert', 
+    'Swap', 'IsNil', 'ArrToSeq', 'CopyStr', 'CopyStrLast', 'NewString', 
+    'Array', 'OpenArray', 'Range', 'Set', 'Seq', 'Ordinal', 
+    'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'Float', 
+    'Float32', 'Float64', 'Bool', 'Char', 'String', 'Cstring', 
+    'Pointer', 'EmptySet', 'IntSetBaseType', 'Nil', 'Expr', 'Stmt', 
+    'TypeDesc', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor', 
+    'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU', 'NaN', 
+    'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild', 'NAdd', 
+    'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal', 'NSymbol', 
+    'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol', 
+    'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree', 
+    'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning', 'NError'
     //[[[end]]]
   );
 
@@ -553,7 +555,7 @@ const
     tyFloat..tyFloat128
   ];
   ConstantDataTypes: TTypeKinds = {@set}[tyArray, tySet, tyTuple];
-  ExportableSymKinds = {@set}[skVar, skConst, skProc, skType,
+  ExportableSymKinds = {@set}[skVar, skConst, skProc, skMethod, skType,
                               skIterator, skMacro, skTemplate, skConverter,
                               skStub];
   PersistentNodeFlags: TNodeFlags = {@set}[
@@ -564,6 +566,7 @@ const
   pragmasPos = 3;
   codePos = 4;
   resultPos = 5;
+  dispatcherPos = 6;
 
 var
   gId: int;
diff --git a/nim/ccgexprs.pas b/nim/ccgexprs.pas
index 97a24caeb..99d36da2d 100755
--- a/nim/ccgexprs.pas
+++ b/nim/ccgexprs.pas
@@ -1285,14 +1285,14 @@ begin
   genAssignment(p, a, c, {@set}[]);
 end;
 
-procedure genIs(p: BProc; n: PNode; var d: TLoc);
+procedure genIs(p: BProc; x: PNode; typ: PType; var d: TLoc); overload;
 var
   a: TLoc;
   dest, t: PType;
   r, nilcheck: PRope;
 begin
-  initLocExpr(p, n.sons[1], a);
-  dest := skipTypes(n.sons[2].typ, abstractPtrs);
+  initLocExpr(p, x, a);
+  dest := skipTypes(typ, abstractPtrs);
   useMagic(p.module, 'isObj');
   r := rdLoc(a);
   nilCheck := nil;
@@ -1313,7 +1313,12 @@ begin
   else
     r := ropef('isObj($1.m_type, $2)',
       [r, genTypeInfo(p.module, dest)]);
-  putIntoDest(p, d, n.typ, r);
+  putIntoDest(p, d, getSysType(tyBool), r);
+end;
+
+procedure genIs(p: BProc; n: PNode; var d: TLoc); overload;
+begin
+  genIs(p, n.sons[1], n.sons[2].typ, d);
 end;
 
 procedure genNewFinalize(p: BProc; e: PNode);
@@ -1330,14 +1335,14 @@ begin
   // This is a little hack: 
   // XXX this is also a bug, if the finalizer expression produces side-effects
   oldModule := p.module;
-  p.module := gmti;
+  p.module := gNimDat;
   InitLocExpr(p, e.sons[2], f);
   p.module := oldModule;
   
   initLoc(b, locExpr, a.t, OnHeap);
   ti := genTypeInfo(p.module, refType);
   
-  appf(gmti.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
+  appf(gNimDat.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
     ti, rdLoc(f)]);
   b.r := ropef('($1) newObj($2, sizeof($3))',
            [getTypeDesc(p.module, refType), ti,
@@ -2164,6 +2169,16 @@ begin
     nkSym: begin
       sym := e.sym;
       case sym.Kind of
+        skMethod: begin
+          if sym.ast.sons[codePos] = nil then begin
+            // we cannot produce code for the dispatcher yet:
+            fillProcLoc(sym);
+            genProcPrototype(p.module, sym);
+          end
+          else
+            genProc(p.module, sym);
+          putLocIntoDest(p, d, sym.loc);
+        end;
         skProc, skConverter: begin
           genProc(p.module, sym);
           if ((sym.loc.r = nil) or (sym.loc.t = nil)) then
diff --git a/nim/ccgstmts.pas b/nim/ccgstmts.pas
index 03d6edb01..97641f0c8 100755
--- a/nim/ccgstmts.pas
+++ b/nim/ccgstmts.pas
@@ -326,7 +326,7 @@ begin
       nkStrLit..nkTripleStrLit: app(s, t.sons[i].strVal);
       nkSym: begin
         sym := t.sons[i].sym;
-        if sym.kind = skProc then begin
+        if sym.kind in [skProc, skMethod] then begin
           initLocExpr(p, t.sons[i], a);
           app(s, rdLoc(a));
         end
@@ -970,12 +970,13 @@ begin
     nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
     nkFromStmt, nkTemplateDef, nkMacroDef: begin end;
     nkPragma: genPragma(p, t);
-    nkProcDef, nkConverterDef: begin
+    nkProcDef, nkMethodDef, nkConverterDef: begin
       if (t.sons[genericParamsPos] = nil) then begin
         prc := t.sons[namePos].sym;
         if not (optDeadCodeElim in gGlobalOptions) and
             not (sfDeadCodeElim in getModule(prc).flags)
-        or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc]) then begin
+        or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc])
+        or (prc.kind = skMethod) then begin
           if (t.sons[codePos] <> nil) or (lfDynamicLib in prc.loc.flags) then begin
             genProc(p.module, prc)
           end
diff --git a/nim/ccgtypes.pas b/nim/ccgtypes.pas
index 8c17514ff..db07a6864 100755
--- a/nim/ccgtypes.pas
+++ b/nim/ccgtypes.pas
@@ -47,7 +47,7 @@ begin
   if result = nil then begin
     if gCmd = cmdCompileToLLVM then begin
       case s.kind of
-        skProc, skConverter, skConst: result := toRope('@'+'');
+        skProc, skMethod, skConverter, skConst: result := toRope('@'+'');
         skVar: begin
           if (sfGlobal in s.flags) then result := toRope('@'+'')
           else result := toRope('%'+'');
@@ -1026,15 +1026,15 @@ begin
     tyEmpty: result := toRope('0'+'');
     tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
     tyInt..tyFloat128, tyVar:
-      genTypeInfoAuxBase(gmti, t, result, toRope('0'+''));
-    tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gmti, t, result);
-    tyArrayConstr, tyArray: genArrayInfo(gmti, t, result);
-    tySet: genSetInfo(gmti, t, result);
-    tyEnum: genEnumInfo(gmti, t, result);
-    tyObject: genObjectInfo(gmti, t, result);
+      genTypeInfoAuxBase(gNimDat, t, result, toRope('0'+''));
+    tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result);
+    tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result);
+    tySet: genSetInfo(gNimDat, t, result);
+    tyEnum: genEnumInfo(gNimDat, t, result);
+    tyObject: genObjectInfo(gNimDat, t, result);
     tyTuple: begin
-      if t.n <> nil then genObjectInfo(gmti, t, result)
-      else genTupleInfo(gmti, t, result);
+      if t.n <> nil then genObjectInfo(gNimDat, t, result)
+      else genTupleInfo(gNimDat, t, result);
     end;
     else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
   end
diff --git a/nim/cgen.pas b/nim/cgen.pas
index 04c19d444..17ae8b461 100755
--- a/nim/cgen.pas
+++ b/nim/cgen.pas
@@ -19,7 +19,7 @@ uses
   nsystem, ast, astalgo, strutils, nhashes, trees, platform, magicsys,
   extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
   lists, types, ccgutils, nos, ntime, ropes, nmath, passes, rodread,
-  wordrecg, rnimsyn, treetab;
+  wordrecg, rnimsyn, treetab, cgmeth;
   
 function cgenPass(): TPass;
 
@@ -131,7 +131,7 @@ var
   gPendingModules: array of BModule = {@ignore} nil {@emit @[]};
     // list of modules that are not finished with code generation
   gForwardedProcsCounter: int = 0;
-  gmti: BModule; // generated type info: no need to initialize: defaults fit
+  gNimDat: BModule; // generated global data
 
 function ropeff(const cformat, llvmformat: string; 
                 const args: array of PRope): PRope;
@@ -557,7 +557,7 @@ begin
   sym := magicsys.getCompilerProc(name);
   if sym <> nil then 
     case sym.kind of
-      skProc, skConverter: genProc(m, sym);
+      skProc, skMethod, skConverter: genProc(m, sym);
       skVar: genVarPrototype(m, sym);
       skType: {@discard} getTypeDesc(m, sym.typ);
       else InternalError('useMagic: ' + name)
@@ -730,6 +730,7 @@ begin
     app(generatedProc, '}' + tnl);
   end;
   app(m.s[cfsProcs], generatedProc);
+  //if prc.kind = skMethod then addMethodToCompile(gNimDat, prc);
 end;
 
 procedure genProcPrototype(m: BModule; sym: PSym);
@@ -746,8 +747,9 @@ begin
     end
   end
   else begin
-    if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then
+    if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then begin
       appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
+    end
   end
 end;
 
@@ -836,18 +838,18 @@ function getFileHeader(const cfilenoext: string): PRope;
 begin
   if optCompileOnly in gGlobalOptions then
     result := ropeff(
-      '/* Generated by the Nimrod Compiler v$1 */$n' +
+      '/* Generated by Nimrod Compiler v$1 */$n' +
       '/*   (c) 2009 Andreas Rumpf */$n',
-      '; Generated by the Nimrod Compiler v$1$n' +
+      '; Generated by Nimrod Compiler v$1$n' +
       ';   (c) 2009 Andreas Rumpf$n',
       [toRope(versionAsString)])
   else
     result := ropeff(
-      '/* Generated by the Nimrod Compiler v$1 */$n' +
+      '/* Generated by Nimrod Compiler v$1 */$n' +
       '/*   (c) 2009 Andreas Rumpf */$n' +
       '/* Compiled for: $2, $3, $4 */$n' +
       '/* Command for C compiler:$n   $5 */$n',
-      '; Generated by the Nimrod Compiler v$1$n' +
+      '; Generated by Nimrod Compiler v$1$n' +
       ';   (c) 2009 Andreas Rumpf$n' +
       '; Compiled for: $2, $3, $4$n' +
       '; Command for LLVM compiler:$n   $5$n',
@@ -1115,15 +1117,15 @@ var
   s: PSym;
 begin
   s := NewSym(skModule, getIdent(moduleName), nil);
-  gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
-  addPendingModule(gmti);
+  gNimDat := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
+  addPendingModule(gNimDat);
   appff(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
                       'declare void $1() noinline$n', [getInitName(s)]);
 end;
 
 function myOpen(module: PSym; const filename: string): PPassContext;
 begin
-  if gmti = nil then registerTypeInfoModule();
+  if gNimDat = nil then registerTypeInfoModule();
   result := newModule(module, filename);
 end;
 
@@ -1132,7 +1134,7 @@ function myOpenCached(module: PSym; const filename: string;
 var
   cfile, cfilenoext, objFile: string;
 begin
-  if gmti = nil then registerTypeInfoModule();
+  if gNimDat = nil then registerTypeInfoModule();
   //MessageOut('cgen.myOpenCached has been called ' + filename);
   cfile := changeFileExt(completeCFilePath(filename), cExt);
   cfilenoext := changeFileExt(cfile, '');
@@ -1177,7 +1179,7 @@ begin
   i := 0;
   while i <= high(m.forwardedProcs) do begin
     // Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
-    // a for loop here
+    // a ``for`` loop here
     prc := m.forwardedProcs[i];
     if sfForward in prc.flags then InternalError(prc.info, 'still forwarded');
     genProcNoForward(m, prc);
@@ -1214,6 +1216,7 @@ function myClose(b: PPassContext; n: PNode): PNode;
 var
   m: BModule;
   i: int;
+  disp: PNode;
 begin
   result := n;
   if b = nil then exit;
@@ -1227,6 +1230,8 @@ begin
       not (sfDeadCodeElim in m.module.flags) then
     finishModule(m);
   if sfMainModule in m.module.flags then begin
+    disp := generateMethodDispatchers();
+    for i := 0 to sonsLen(disp)-1 do genProcAux(gNimDat, disp.sons[i].sym);
     genMainProc(m);
     // we need to process the transitive closure because recursive module
     // deps are allowed (and the system module is processed in the wrong
@@ -1241,7 +1246,7 @@ begin
       not (sfDeadCodeElim in m.module.flags) then
     writeModule(m);
   if sfMainModule in m.module.flags then 
-    writeMapping(gMapping);  
+    writeMapping(gMapping);
 end;
 
 function cgenPass(): TPass;
diff --git a/nim/cgmeth.pas b/nim/cgmeth.pas
new file mode 100644
index 000000000..6b9335c4c
--- /dev/null
+++ b/nim/cgmeth.pas
@@ -0,0 +1,269 @@
+//
+//
+//           The Nimrod Compiler
+//        (c) Copyright 2009 Andreas Rumpf
+//
+//    See the file "copying.txt", included in this
+//    distribution, for details about the copyright.
+//
+
+unit cgmeth;
+
+// This module implements code generation for multi methods.
+  
+interface
+
+{$include 'config.inc'}
+
+uses
+  sysutils, nsystem, 
+  options, ast, astalgo, msgs, idents, rnimsyn, types, magicsys;
+
+procedure methodDef(s: PSym);
+function methodCall(n: PNode): PNode;
+function generateMethodDispatchers(): PNode;
+
+implementation
+
+const
+  skipPtrs = {@set}[tyVar, tyPtr, tyRef, tyGenericInst];
+
+function genConv(n: PNode; d: PType; downcast: bool): PNode;
+var
+  dest, source: PType;
+  diff: int;
+begin
+  dest := skipTypes(d, abstractPtrs);
+  source := skipTypes(n.typ, abstractPtrs);
+  if (source.kind = tyObject) and (dest.kind = tyObject) then begin
+    diff := inheritanceDiff(dest, source);
+    if diff = high(int) then InternalError(n.info, 'cgmeth.genConv');
+    if diff < 0 then begin
+      result := newNodeIT(nkObjUpConv, n.info, d);
+      addSon(result, n);
+      if downCast then
+        InternalError(n.info, 'cgmeth.genConv: no upcast allowed');
+    end
+    else if diff > 0 then begin
+      result := newNodeIT(nkObjDownConv, n.info, d);
+      addSon(result, n);
+      if not downCast then
+        InternalError(n.info, 'cgmeth.genConv: no downcast allowed');
+    end
+    else result := n
+  end
+  else result := n
+end;
+
+function methodCall(n: PNode): PNode;
+var
+  disp: PSym;
+  i: int;
+begin
+  result := n;
+  disp := lastSon(result.sons[0].sym.ast).sym;
+  result.sons[0].sym := disp;
+  for i := 1 to sonsLen(result)-1 do
+    result.sons[i] := genConv(result.sons[i], disp.typ.sons[i], true)
+end;
+
+var
+  gMethods: array of TSymSeq;
+
+function sameMethodBucket(a, b: PSym): bool;
+var
+  i: int;
+  aa, bb: PType;
+begin
+  result := false;
+  if a.name.id <> b.name.id then exit;
+  if sonsLen(a.typ) <> sonsLen(b.typ) then exit;
+  // check for return type:
+  if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]) then exit;
+  for i := 1 to sonsLen(a.typ)-1 do begin
+    aa := a.typ.sons[i];
+    bb := b.typ.sons[i];
+    while true do begin
+      aa := skipTypes(aa, {@set}[tyGenericInst]);
+      bb := skipTypes(bb, {@set}[tyGenericInst]);
+      if (aa.kind = bb.kind) and (aa.kind in [tyVar, tyPtr, tyRef]) then begin
+        aa := aa.sons[0];
+        bb := bb.sons[0];
+      end
+      else
+        break
+    end;
+    if sameType(aa, bb)
+    or (aa.kind = tyObject) and (bb.kind = tyObject)
+        and (inheritanceDiff(bb, aa) < 0) then begin end
+    else exit;
+  end;
+  result := true
+end;
+
+procedure methodDef(s: PSym);
+var
+  i, L, q: int;
+  disp: PSym;
+begin 
+  L := length(gMethods);
+  for i := 0 to L-1 do begin
+    if sameMethodBucket(gMethods[i][0], s) then begin
+    {@ignore}
+      q := length(gMethods[i]);
+      setLength(gMethods[i], q+1);
+      gMethods[i][q] := s;
+    {@emit
+      add(gMethods[i], s);
+    }
+      // store a symbol to the dispatcher:
+      addSon(s.ast, lastSon(gMethods[i][0].ast));
+      exit
+    end
+  end;
+{@ignore}
+  setLength(gMethods, L+1);
+  setLength(gMethods[L], 1);
+  gMethods[L][0] := s;
+{@emit
+  add(gMethods, @[s]);
+}
+  // create a new dispatcher:
+  disp := copySym(s);
+  disp.typ := copyType(disp.typ, disp.typ.owner, false);
+  if disp.typ.callConv = ccInline then disp.typ.callConv := ccDefault;
+  disp.ast := copyTree(s.ast);
+  disp.ast.sons[codePos] := nil;
+  if s.typ.sons[0] <> nil then
+    disp.ast.sons[resultPos].sym := copySym(s.ast.sons[resultPos].sym);
+  addSon(s.ast, newSymNode(disp));
+end;
+
+function relevantCol(methods: TSymSeq; col: int): bool;
+var
+  t: PType;
+  i: int;
+begin
+  // returns true iff the position is relevant
+  t := methods[0].typ.sons[col];
+  result := false;
+  if skipTypes(t, skipPtrs).kind = tyObject then 
+    for i := 1 to high(methods) do
+      if not SameType(methods[i].typ.sons[col], t) then begin
+        result := true; exit
+      end
+end;
+
+function cmpSignatures(a, b: PSym; const relevantCols: TIntSet): int;
+var
+  col, d: int;
+  aa, bb: PType;
+begin
+  result := 0;
+  for col := 1 to sonsLen(a.typ)-1 do
+    if intSetContains(relevantCols, col) then begin
+      aa := skipTypes(a.typ.sons[col], skipPtrs);
+      bb := skipTypes(b.typ.sons[col], skipPtrs);
+      d := inheritanceDiff(aa, bb);
+      if (d <> high(int)) then begin
+        result := d; exit
+      end
+    end
+end;
+
+procedure sortBucket(var a: TSymSeq; const relevantCols: TIntSet);
+// we use shellsort here; fast and simple
+var
+  N, i, j, h: int;
+  v: PSym;
+begin
+  N := length(a);
+  h := 1; repeat h := 3*h+1; until h > N;
+  repeat
+    h := h div 3;
+    for i := h to N-1 do begin
+      v := a[i]; j := i;
+      while cmpSignatures(a[j-h], v, relevantCols) >= 0 do begin
+        a[j] := a[j-h]; j := j - h;
+        if j < h then break
+      end;
+      a[j] := v;
+    end;
+  until h = 1
+end;
+
+function genDispatcher(methods: TSymSeq; const relevantCols: TIntSet): PSym;
+var
+  disp, cond, call, ret, a, isn: PNode;
+  base, curr, ands, iss: PSym;
+  meth, col, paramLen: int;
+begin
+  base := lastSon(methods[0].ast).sym;
+  result := base;
+  paramLen := sonsLen(base.typ);
+  disp := newNodeI(nkIfStmt, base.info);
+  ands := getSysSym('and');
+  iss := getSysSym('is');
+  for meth := 0 to high(methods) do begin
+    curr := methods[meth];
+    // generate condition:
+    cond := nil;
+    for col := 1 to paramLen-1 do begin
+      if IntSetContains(relevantCols, col) then begin
+        isn := newNodeIT(nkCall, base.info, getSysType(tyBool));
+        addSon(isn, newSymNode(iss));
+        addSon(isn, newSymNode(base.typ.n.sons[col].sym));
+        addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]));
+        if cond <> nil then begin
+          a := newNodeIT(nkCall, base.info, getSysType(tyBool));
+          addSon(a, newSymNode(ands));
+          addSon(a, cond);
+          addSon(a, isn);
+          cond := a
+        end
+        else
+          cond := isn
+      end
+    end;
+    // generate action:
+    call := newNodeI(nkCall, base.info);
+    addSon(call, newSymNode(curr));
+    for col := 1 to paramLen-1 do begin
+      addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
+                           curr.typ.sons[col], false));
+    end;
+    if base.typ.sons[0] <> nil then begin
+      a := newNodeI(nkAsgn, base.info);
+      addSon(a, newSymNode(base.ast.sons[resultPos].sym));
+      addSon(a, call);
+      ret := newNodeI(nkReturnStmt, base.info);
+      addSon(ret, a);
+    end
+    else
+      ret := call;
+    a := newNodeI(nkElifBranch, base.info);
+    addSon(a, cond);
+    addSon(a, ret);
+    addSon(disp, a);
+  end;
+  result.ast.sons[codePos] := disp;
+end;
+
+function generateMethodDispatchers(): PNode;
+var
+  bucket, col: int;
+  relevantCols: TIntSet;
+begin
+  result := newNode(nkStmtList);
+  for bucket := 0 to length(gMethods)-1 do begin
+    IntSetInit(relevantCols);
+    for col := 1 to sonsLen(gMethods[bucket][0].typ)-1 do
+      if relevantCol(gMethods[bucket], col) then IntSetIncl(relevantCols, col);
+    sortBucket(gMethods[bucket], relevantCols);
+    addSon(result, newSymNode(genDispatcher(gMethods[bucket], relevantCols)));
+  end
+end;
+
+initialization
+  {@emit gMethods := @[]; }
+end.
diff --git a/nim/docgen.pas b/nim/docgen.pas
index 39a9069c7..4d94a2a68 100755
--- a/nim/docgen.pas
+++ b/nim/docgen.pas
@@ -1036,6 +1036,7 @@ begin
   case n.kind of
     nkCommentStmt:  app(d.modDesc, genComment(d, n));
     nkProcDef:      genItem(d, n, n.sons[namePos], skProc);
+    nkMethodDef:    genItem(d, n, n.sons[namePos], skMethod);
     nkIteratorDef:  genItem(d, n, n.sons[namePos], skIterator);
     nkMacroDef:     genItem(d, n, n.sons[namePos], skMacro);
     nkTemplateDef:  genItem(d, n, n.sons[namePos], skTemplate);
diff --git a/nim/ecmasgen.pas b/nim/ecmasgen.pas
index 73faa4ddb..8edd2605a 100755
--- a/nim/ecmasgen.pas
+++ b/nim/ecmasgen.pas
@@ -1717,7 +1717,7 @@ begin
     nkTypeSection, nkCommentStmt, nkIteratorDef,
     nkIncludeStmt, nkImportStmt,
     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: begin end;
-    nkProcDef, nkConverterDef: begin
+    nkProcDef, nkMethodDef, nkConverterDef: begin
       if (n.sons[genericParamsPos] = nil) then begin
         prc := n.sons[namePos].sym;
         if (n.sons[codePos] <> nil) and not (lfNoDecl in prc.loc.flags) then
diff --git a/nim/evals.pas b/nim/evals.pas
index b88fa3304..3787ff371 100755
--- a/nim/evals.pas
+++ b/nim/evals.pas
@@ -938,23 +938,6 @@ begin
   result := emptyNode;
 end;
 
-function evalAppendSeqSeq(c: PEvalContext; n: PNode): PNode;
-var
-  a, b: PNode;
-  i: int;
-begin
-  result := evalAux(c, n.sons[1]);
-  if result.kind = nkExceptBranch then exit;
-  a := result;
-  result := evalAux(c, n.sons[2]);
-  if result.kind = nkExceptBranch then exit;
-  b := result;
-  if a.kind = nkBracket then
-    for i := 0 to sonsLen(b)-1 do addSon(a, copyTree(b.sons[i]))
-  else InternalError(n.info, 'evalAppendSeqSeq');
-  result := emptyNode;
-end;
-
 function evalRepr(c: PEvalContext; n: PNode): PNode;
 begin
   result := evalAux(c, n.sons[1]);
@@ -993,7 +976,6 @@ begin
     mAppendStrCh: result := evalAppendStrCh(c, n);
     mAppendStrStr: result := evalAppendStrStr(c, n);
     mAppendSeqElem: result := evalAppendSeqElem(c, n);
-    mAppendSeqSeq: result := evalAppendSeqSeq(c, n);
 
     mNLen: begin
       result := evalAux(c, n.sons[1]);
@@ -1338,7 +1320,7 @@ begin
         end
       end
     end;
-    nkProcDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection,
+    nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma, nkTypeSection,
     nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
     nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
     nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
diff --git a/nim/highlite.pas b/nim/highlite.pas
index ec5374663..fa760d2a2 100755
--- a/nim/highlite.pas
+++ b/nim/highlite.pas
@@ -264,18 +264,37 @@ begin
         while not (g.buf[pos] in [#0, #10, #13]) do inc(pos);
       end;
       'a'..'z', 'A'..'Z', '_', #128..#255: begin
-        if (g.buf[pos+1] = '"') and (g.buf[pos] in ['r', 'R']) then begin
-          g.kind := gtRawData;
-          inc(pos, 2);
-          while not (g.buf[pos] in [#0, '"', #10, #13]) do inc(pos);
-          if g.buf[pos] = '"' then inc(pos);
+        id := '';
+        while g.buf[pos] in scanner.SymChars+['_'] do begin
+          addChar(id, g.buf[pos]);
+          inc(pos)
+        end;
+        if (g.buf[pos] = '"') then begin
+          if (g.buf[pos+1] = '"') and (g.buf[pos+2] = '"') then begin
+            inc(pos, 3);
+            g.kind := gtLongStringLit;
+            while true do begin
+              case g.buf[pos] of
+                #0: break;
+                '"': begin
+                  inc(pos);
+                  if (g.buf[pos] = '"') and (g.buf[pos+1] = '"') then begin
+                    inc(pos, 2);
+                    break
+                  end
+                end;
+                else inc(pos);
+              end
+            end
+          end
+          else begin
+            g.kind := gtRawData;
+            inc(pos);
+            while not (g.buf[pos] in [#0, '"', #10, #13]) do inc(pos);
+            if g.buf[pos] = '"' then inc(pos);
+          end
         end
         else begin
-          id := '';
-          while g.buf[pos] in scanner.SymChars+['_'] do begin
-            addChar(id, g.buf[pos]);
-            inc(pos)
-          end;
           g.kind := nimGetKeyword(id);
         end
       end;
diff --git a/nim/importer.pas b/nim/importer.pas
index fdb72fd4b..d031ffe3a 100755
--- a/nim/importer.pas
+++ b/nim/importer.pas
@@ -113,7 +113,7 @@ begin
     InternalError(ident.info, 'importSymbol: 2');
   // for an enumeration we have to add all identifiers
   case s.Kind of
-    skProc, skIterator, skMacro, skTemplate, skConverter: begin
+    skProc, skMethod, skIterator, skMacro, skTemplate, skConverter: begin
       // for a overloadable syms add all overloaded routines
       e := InitIdentIter(it, fromMod.tab, s.name);
       while e <> nil do begin
@@ -155,6 +155,8 @@ begin
   for i := 0 to sonsLen(n)-1 do begin
     f := getModuleFile(n.sons[i]);
     m := gImportModule(f);
+    if sfDeprecated in m.flags then
+      liMessage(n.sons[i].info, warnDeprecated, m.name.s);
     // ``addDecl`` needs to be done before ``importAllSymbols``!
     addDecl(c, m); // add symbol to symbol table of module
     importAllSymbols(c, m);
diff --git a/nim/lookups.pas b/nim/lookups.pas
index 5caceaf46..d68095635 100755
--- a/nim/lookups.pas
+++ b/nim/lookups.pas
@@ -54,7 +54,7 @@ implementation
 function getSymRepr(s: PSym): string;
 begin
   case s.kind of
-    skProc, skConverter, skIterator: result := getProcHeader(s);
+    skProc, skMethod, skConverter, skIterator: result := getProcHeader(s);
     else result := s.name.s
   end
 end;
@@ -72,7 +72,7 @@ begin
       liMessage(s.info, errImplOfXexpected, getSymRepr(s))
     else if ([sfUsed, sfInInterface] * s.flags = []) and
             (optHints in s.options) then // BUGFIX: check options in s!
-      if not (s.kind in [skForVar, skParam, skUnknown]) then
+      if not (s.kind in [skForVar, skParam, skMethod, skUnknown]) then
         liMessage(s.info, hintXDeclaredButNotUsed, getSymRepr(s));
     s := NextIter(it, tab.stack[tab.tos-1]);
   end;
diff --git a/nim/magicsys.pas b/nim/magicsys.pas
index d9dde8871..f4e4beafe 100755
--- a/nim/magicsys.pas
+++ b/nim/magicsys.pas
@@ -30,6 +30,8 @@ procedure registerCompilerProc(s: PSym);
 procedure InitSystem(var tab: TSymTab);
 procedure FinishSystem(const tab: TStrTable);
 
+function getSysSym(const name: string): PSym;
+
 implementation
 
 var
@@ -48,14 +50,16 @@ begin
   result.align := size;
 end;
 
+function getSysSym(const name: string): PSym;
+begin
+  result := StrTableGet(systemModule.tab, getIdent(name));
+  if result = nil then rawMessage(errSystemNeeds, name);
+  if result.kind = skStub then loadStub(result); 
+end;
+
 function sysTypeFromName(const name: string): PType;
-var
-  s: PSym;
 begin
-  s := StrTableGet(systemModule.tab, getIdent(name));
-  if s = nil then rawMessage(errSystemNeeds, name);
-  if s.kind = skStub then loadStub(s); 
-  result := s.typ;
+  result := getSysSym(name).typ;
 end;
 
 function getSysType(const kind: TTypeKind): PType;
diff --git a/nim/nhashes.pas b/nim/nhashes.pas
index 0564f6f47..95bfd62f5 100755
--- a/nim/nhashes.pas
+++ b/nim/nhashes.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.
diff --git a/nim/nversion.pas b/nim/nversion.pas
index add85acd1..b2ba371f6 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.0';
+  VersionAsString = '0.8.1';
   VersionMajor = 0;
   VersionMinor = 8;
-  VersionPatch = 0;
+  VersionPatch = 1;
   //[[[[end]]]]

 

 implementation

diff --git a/nim/pasparse.pas b/nim/pasparse.pas
index 8a73feca5..c44c735d6 100755
--- a/nim/pasparse.pas
+++ b/nim/pasparse.pas
@@ -61,7 +61,7 @@ const
     ('len',          'length'),
     ('setlength',    'setlen')
   );
-  nimReplacements: array [1..32] of TReplaceTuple = (
+  nimReplacements: array [1..34] of TReplaceTuple = (
     ('nimread',      'read'),
     ('nimwrite',     'write'),
     ('nimclosefile', 'close'),
@@ -96,7 +96,10 @@ const
     ('snil', 'nil'),
     ('tostringf', '$'+''),
     ('ttextfile', 'tfile'),
-    ('tbinaryfile', 'tfile') {,
+    ('tbinaryfile', 'tfile'),
+    ('strstart', '0'+''),
+    ('nl', '"\n"')
+    {,
     ('NL', '"\n"'),
     ('tabulator', '''\t'''),
     ('esc', '''\e'''),
diff --git a/nim/passaux.pas b/nim/passaux.pas
index 8b052257f..7898d8278 100755
--- a/nim/passaux.pas
+++ b/nim/passaux.pas
@@ -56,7 +56,7 @@ begin
     nkStmtList: begin
       for i := 0 to sonsLen(n)-1 do {@discard} cleanup(c, n.sons[i]);
     end;
-    nkProcDef: begin
+    nkProcDef, nkMethodDef: begin
       if (n.sons[namePos].kind = nkSym) then begin
         s := n.sons[namePos].sym;
         if not (sfDeadCodeElim in getModule(s).flags) and
diff --git a/nim/passes.pas b/nim/passes.pas
index f5dff3559..8d961e5d3 100755
--- a/nim/passes.pas
+++ b/nim/passes.pas
@@ -67,7 +67,7 @@ implementation
 
 function astNeeded(s: PSym): bool;
 begin
-  if (s.kind = skProc)
+  if (s.kind in [skMethod, skProc])
   and ([sfCompilerProc, sfCompileTime] * s.flags = [])
   and (s.typ.callConv <> ccInline)
   and (s.ast.sons[genericParamsPos] = nil) then
diff --git a/nim/pnimsyn.pas b/nim/pnimsyn.pas
index dae7de539..724a85a38 100755
--- a/nim/pnimsyn.pas
+++ b/nim/pnimsyn.pas
@@ -889,7 +889,7 @@ end;
 
 function parseImportStmt(var p: TParser): PNode;
 var
-  a, b: PNode;
+  a: PNode;
 begin
   result := newNodeP(nkImportStmt, p);
   getTok(p); // skip `import`
@@ -1535,9 +1535,9 @@ begin
   end
 end;
 
-function parseRecordPart(var p: TParser): PNode; forward;
+function parseObjectPart(var p: TParser): PNode; forward;
 
-function parseRecordWhen(var p: TParser): PNode;
+function parseObjectWhen(var p: TParser): PNode;
 var
   branch: PNode;
 begin
@@ -1549,7 +1549,7 @@ begin
     addSon(branch, parseExpr(p));
     eat(p, tkColon);
     skipComment(p, branch);
-    addSon(branch, parseRecordPart(p));
+    addSon(branch, parseObjectPart(p));
     skipComment(p, branch);
     addSon(result, branch);
     if p.tok.tokType <> tkElif then break
@@ -1558,12 +1558,12 @@ begin
     branch := newNodeP(nkElse, p);
     eat(p, tkElse); eat(p, tkColon);
     skipComment(p, branch);
-    addSon(branch, parseRecordPart(p));
+    addSon(branch, parseObjectPart(p));
     addSon(result, branch);
   end
 end;
 
-function parseRecordCase(var p: TParser): PNode;
+function parseObjectCase(var p: TParser): PNode;
 var
   a, b: PNode;
 begin
@@ -1591,13 +1591,13 @@ begin
       else break;
     end;
     skipComment(p, b);
-    addSon(b, parseRecordPart(p));
+    addSon(b, parseObjectPart(p));
     addSon(result, b);
     if b.kind = nkElse then break;
   end
 end;
 
-function parseRecordPart(var p: TParser): PNode;
+function parseObjectPart(var p: TParser): PNode;
 begin
   case p.tok.tokType of
     tkInd: begin
@@ -1608,7 +1608,7 @@ begin
         case p.tok.tokType of
           tkSad: getTok(p);
           tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin
-            addSon(result, parseRecordPart(p));
+            addSon(result, parseObjectPart(p));
           end;
           tkDed: begin getTok(p); break end;
           tkEof: break;
@@ -1620,8 +1620,8 @@ begin
       end;
       popInd(p.lex^);
     end;
-    tkWhen: result := parseRecordWhen(p);
-    tkCase: result := parseRecordCase(p);
+    tkWhen: result := parseObjectWhen(p);
+    tkCase: result := parseObjectCase(p);
     tkSymbol, tkAccent: begin
       result := parseIdentColonEquals(p, {@set}[withPragma]);
       skipComment(p, result);
@@ -1634,11 +1634,11 @@ begin
   end
 end;
 
-function parseRecordOrObject(var p: TParser; kind: TNodeKind): PNode;
+function parseObject(var p: TParser): PNode;
 var
   a: PNode;
 begin
-  result := newNodeP(kind, p);
+  result := newNodeP(nkObjectTy, p);
   getTok(p);
   if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
   else addSon(result, nil);
@@ -1646,14 +1646,11 @@ begin
     a := newNodeP(nkOfInherit, p);
     getTok(p);
     addSon(a, parseTypeDesc(p));
-    if kind = nkObjectTy then
-      addSon(result, a)
-    else
-      parMessage(p, errInheritanceOnlyWithNonFinalObjects);
+    addSon(result, a);
   end
   else addSon(result, nil);
   skipComment(p, result);
-  addSon(result, parseRecordPart(p));
+  addSon(result, parseObjectPart(p));
 end;
 
 function parseDistinct(var p: TParser): PNode;
@@ -1675,7 +1672,7 @@ begin
   if p.tok.tokType = tkEquals then begin
     getTok(p); optInd(p, result);
     case p.tok.tokType of
-      tkObject: a := parseRecordOrObject(p, nkObjectTy);
+      tkObject: a := parseObject(p);
       tkEnum: a := parseEnum(p);
       tkDistinct: a := parseDistinct(p);
       else a := parseTypeDesc(p);
@@ -1754,6 +1751,7 @@ begin
     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);
diff --git a/nim/pragmas.pas b/nim/pragmas.pas
index 5ec706f19..fc354cd2c 100755
--- a/nim/pragmas.pas
+++ b/nim/pragmas.pas
@@ -30,6 +30,7 @@ const
     wCppMethod, wDeprecated, wVarargs, wCompileTime, wMerge,
     wBorrow];
   converterPragmas = procPragmas;
+  methodPragmas = procPragmas;
   macroPragmas = {@set}[FirstCallConv..LastCallConv,
     wImportc, wExportc, wNodecl, wMagic, wNosideEffect,
     wCompilerProc, wDeprecated, wTypeCheck];
@@ -42,7 +43,7 @@ const
     wHint, wWarning, wError, wFatal, wDefine, wUndef,
     wCompile, wLink, wLinkSys, wPure,
     wPush, wPop, wBreakpoint, wCheckpoint,
-    wPassL, wPassC, wDeadCodeElim];
+    wPassL, wPassC, wDeadCodeElim, wDeprecated];
   lambdaPragmas = {@set}[FirstCallConv..LastCallConv,
     wImportc, wExportc, wNodecl, wNosideEffect, wSideEffect, 
     wNoreturn, wDynLib, wHeader, wPure, wDeprecated];
@@ -545,7 +546,8 @@ begin
           end;
           wDeprecated: begin
             noVal(it);
-            include(sym.flags, sfDeprecated);
+            if sym <> nil then include(sym.flags, sfDeprecated)
+            else include(c.module.flags, sfDeprecated);
           end;
           wVarargs: begin
             noVal(it);
diff --git a/nim/rnimsyn.pas b/nim/rnimsyn.pas
index b3271a3bb..1ad1c9408 100755
--- a/nim/rnimsyn.pas
+++ b/nim/rnimsyn.pas
@@ -1235,6 +1235,10 @@ begin
       putWithSpace(g, tkProc, 'proc');
       gproc(g, n);
     end;
+    nkMethodDef: begin
+      putWithSpace(g, tkMethod, 'method');
+      gproc(g, n);
+    end;
     nkIteratorDef: begin
       putWithSpace(g, tkIterator, 'iterator');
       gproc(g, n);
diff --git a/nim/rodwrite.pas b/nim/rodwrite.pas
index 72d5c893d..c71eda7e3 100755
--- a/nim/rodwrite.pas
+++ b/nim/rodwrite.pas
@@ -519,7 +519,7 @@ begin
       s := n.sons[namePos].sym;
       addInterfaceSym(w, s);    
     end; 
-    nkProcDef, nkIteratorDef, nkConverterDef: begin
+    nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef: begin
       s := n.sons[namePos].sym;
       if s = nil then InternalError(n.info, 'rodwrite.process');
       if (n.sons[codePos] <> nil) or (s.magic <> mNone)
diff --git a/nim/sem.pas b/nim/sem.pas
index 029ba869f..64cdbd436 100755
--- a/nim/sem.pas
+++ b/nim/sem.pas
@@ -56,6 +56,12 @@ begin
   result.info := n.info;
 end;
 
+procedure markUsed(n: PNode; s: PSym);
+begin
+  include(s.flags, sfUsed);
+  if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);  
+end;
+
 function semIdentVis(c: PContext; kind: TSymKind; n: PNode;
                      const allowed: TSymFlags): PSym; forward;
 // identifier with visability
@@ -130,7 +136,7 @@ var
   p: PEvalContext;
   s: PStackFrame;
 begin
-  include(sym.flags, sfUsed);
+  markUsed(n, sym);
   p := newEvalContext(c.module, '', false);
   s := newStackFrame();
   s.call := n;
@@ -179,7 +185,8 @@ begin
     it := c.generics.sons[i].sons[1];
     if it.kind <> nkSym then InternalError('addCodeForGenerics');
     prc := it.sym;
-    if (prc.kind in [skProc, skConverter]) and (prc.magic = mNone) then begin
+    if (prc.kind in [skProc, skMethod, skConverter])
+    and (prc.magic = mNone) then begin
       if (prc.ast = nil) or (prc.ast.sons[codePos] = nil) then 
         InternalError(prc.info, 'no code for ' + prc.name.s);
       addSon(n, prc.ast);
diff --git a/nim/semexprs.pas b/nim/semexprs.pas
index 2fd0d6843..43eb9ac6f 100755
--- a/nim/semexprs.pas
+++ b/nim/semexprs.pas
@@ -13,7 +13,7 @@
 function semTemplateExpr(c: PContext; n: PNode; s: PSym;
                          semCheck: bool = true): PNode;
 begin
-  include(s.flags, sfUsed);
+  markUsed(n, s);
   pushInfoContext(n.info);
   result := evalTemplate(c, n, s);
   if semCheck then
@@ -47,8 +47,6 @@ var
   diff: int;
 begin
   diff := inheritanceDiff(castDest, src);
-  //if diff = 0 then
-  //  liMessage(info, hintConvToBaseNotNeeded)
   if diff = high(int) then
     liMessage(info, errGenerated,
       format(MsgKindToString(errIllegalConvFromXtoY),
@@ -136,7 +134,7 @@ begin
   else begin
     for i := 0 to sonsLen(op)-1 do begin
       if sameType(result.typ, op.sons[i].typ) then begin
-        include(op.sons[i].sym.flags, sfUsed);
+        markUsed(n, op.sons[i].sym);
         result := op.sons[i]; exit
       end
     end;
@@ -453,8 +451,7 @@ const
   FakeVarParams = {@set}[mNew, mNewFinalize, mInc, ast.mDec, mIncl,
                          mExcl, mSetLengthStr, mSetLengthSeq,
                          mAppendStrCh, mAppendStrStr, mSwap,
-                         mAppendSeqElem, mAppendSeqSeq,
-                         mNewSeq];
+                         mAppendSeqElem, mNewSeq];
 var
   i: int;
   t: PType;
@@ -474,7 +471,7 @@ var
   callee: PSym;
 begin
   if not (efWantIterator in flags) then 
-    result := semDirectCall(c, n, {@set}[skProc, skConverter])
+    result := semDirectCall(c, n, {@set}[skProc, skMethod, skConverter])
   else
     result := semDirectCall(c, n, {@set}[skIterator]);
   if result <> nil then begin
@@ -586,17 +583,23 @@ begin
   result := n;
 end;
 
-function LookUpForDefined(c: PContext; n: PNode): PSym;
+function LookUpForDefined(c: PContext; n: PNode; onlyCurrentScope: bool): PSym;
 var
   m: PSym;
   ident: PIdent;
 begin
   case n.kind of
-    nkIdent: result := SymtabGet(c.Tab, n.ident); // no need for stub loading
+    nkIdent: begin
+      if onlyCurrentScope then
+        result := SymtabLocalGet(c.tab, n.ident)
+      else
+        result := SymtabGet(c.Tab, n.ident); // no need for stub loading
+    end;
     nkDotExpr, nkQualified: begin
-      checkSonsLen(n, 2);
       result := nil;
-      m := LookupForDefined(c, n.sons[0]);
+      if onlyCurrentScope then exit;
+      checkSonsLen(n, 2);
+      m := LookupForDefined(c, n.sons[0], onlyCurrentScope);
       if (m <> nil) and (m.kind = skModule) then begin
         if (n.sons[1].kind = nkIdent) then begin
           ident := n.sons[1].ident;
@@ -612,7 +615,7 @@ begin
     end;
     nkAccQuoted: begin
       checkSonsLen(n, 1);
-      result := lookupForDefined(c, n.sons[0]);
+      result := lookupForDefined(c, n.sons[0], onlyCurrentScope);
     end
     else begin
       liMessage(n.info, errIdentifierExpected, renderTree(n));
@@ -621,14 +624,14 @@ begin
   end
 end;
 
-function semDefined(c: PContext; n: PNode): PNode;
+function semDefined(c: PContext; n: PNode; onlyCurrentScope: bool): PNode;
 begin
   checkSonsLen(n, 2);
   result := newIntNode(nkIntLit, 0);
   // we replace this node by a 'true' or 'false' node
-  if LookUpForDefined(c, n.sons[1]) <> nil then
+  if LookUpForDefined(c, n.sons[1], onlyCurrentScope) <> nil then
     result.intVal := 1
-  else if (n.sons[1].kind = nkIdent)
+  else if not onlyCurrentScope and (n.sons[1].kind = nkIdent)
       and condsyms.isDefined(n.sons[1].ident) then
     result.intVal := 1;
   result.info := n.info;
@@ -647,8 +650,8 @@ function semMagic(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
 begin
   result := n;
   case s.magic of // magics that need special treatment
-    mDefined: result := semDefined(c, setMs(n, s));
-    mDefinedInScope: result := semDefinedInScope(c, setMs(n, s));
+    mDefined: result := semDefined(c, setMs(n, s), false);
+    mDefinedInScope: result := semDefined(c, setMs(n, s), true);
     mLow:     result := semLowHigh(c, setMs(n, s), mLow);
     mHigh:    result := semLowHigh(c, setMs(n, s), mHigh);
     mSizeOf:  result := semSizeof(c, setMs(n, s));
@@ -658,11 +661,6 @@ begin
   end;
 end;
 
-procedure checkDeprecated(n: PNode; s: PSym);
-begin
-  if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);  
-end;
-
 function isTypeExpr(n: PNode): bool;
 begin
   case n.kind of
@@ -795,7 +793,7 @@ begin
       result := newSymNode(f);
       result.info := n.info;
       result.typ := ty;
-      checkDeprecated(n, f);
+      markUsed(n, f);
     end
     else
       liMessage(n.sons[1].info, errEnumHasNoValueX, i.s);
@@ -823,7 +821,7 @@ begin
         n.sons[0] := makeDeref(n.sons[0]);
         n.sons[1] := newSymNode(f); // we now have the correct field
         n.typ := f.typ;
-        checkDeprecated(n, f);
+        markUsed(n, f);
         if check = nil then result := n
         else begin
           check.sons[0] := n;
@@ -841,7 +839,7 @@ begin
       n.sons[1] := newSymNode(f);
       n.typ := f.typ;
       result := n;
-      checkDeprecated(n, f);
+      markUsed(n, f);
       exit
     end
   end;
@@ -851,7 +849,7 @@ begin
   //if (f <> nil) and (f.kind = skStub) then loadStub(f);
   // ``loadStub`` is not correct here as we don't care for ``f`` really
   if (f <> nil) then begin
-    // BUGFIX: do not check for (f.kind in [skProc, skIterator]) here
+    // BUGFIX: do not check for (f.kind in [skProc, skMethod, skIterator]) here
     result := newNodeI(nkDotCall, n.info);
     // This special node kind is to merge with the call handler in `semExpr`.
     addSon(result, newIdentNode(i, n.info));
@@ -1145,7 +1143,6 @@ begin
     a := n.sons[0];
   s := qualifiedLookup(c, a, false);
   if (s <> nil) then begin
-    checkDeprecated(n, s);
     case s.kind of
       skMacro: result := semMacroExpr(c, n, s, semCheck);
       skTemplate: begin
@@ -1176,7 +1173,7 @@ begin
   if (s.kind = skType) and not (efAllowType in flags) then
     liMessage(n.info, errATypeHasNoValue);
   case s.kind of
-    skProc, skIterator, skConverter: begin
+    skProc, skMethod, skIterator, skConverter: begin
       if (s.magic <> mNone) then
         liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
       result := symChoice(c, n, s);
@@ -1194,7 +1191,7 @@ begin
         copy `x`'s AST into each context, so that the type fixup phase can
         deal with two different ``[]``.
       *)
-      include(s.flags, sfUsed);
+      markUsed(n, s);
       if s.typ.kind in ConstAbstractTypes then begin
         result := copyTree(s.ast);
         result.info := n.info;
@@ -1208,7 +1205,7 @@ begin
     skMacro: result := semMacroExpr(c, n, s);
     skTemplate: result := semTemplateExpr(c, n, s);
     skVar: begin
-      include(s.flags, sfUsed);
+      markUsed(n, s);
       // if a proc accesses a global variable, it is not side effect free
       if sfGlobal in s.flags then include(c.p.owner.flags, sfSideEffect);
       result := newSymNode(s);
@@ -1219,12 +1216,11 @@ begin
       result := semExpr(c, s.ast);
     end
     else begin
-      include(s.flags, sfUsed);
+      markUsed(n, s);
       result := newSymNode(s);
       result.info := n.info;
     end
   end;
-  checkDeprecated(n, s);
 end;
 
 function semDotExpr(c: PContext; n: PNode; flags: TExprFlags): PNode;
@@ -1259,7 +1255,7 @@ begin
       if (s.kind = skType) and not (efAllowType in flags) then
         liMessage(n.info, errATypeHasNoValue);
       if (s.magic <> mNone) and
-          (s.kind in [skProc, skIterator, skConverter]) then
+          (s.kind in [skProc, skMethod, skIterator, skConverter]) then
         liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *)
       // because of the changed symbol binding, this does not mean that we
       // don't have to check the symbol for semantics here again!
@@ -1296,18 +1292,16 @@ begin
       checkMinSonsLen(n, 1);
       s := qualifiedLookup(c, n.sons[0], false);
       if (s <> nil) then begin
-        checkDeprecated(n, s);
         case s.kind of
           skMacro: result := semMacroExpr(c, n, s);
           skTemplate: result := semTemplateExpr(c, n, s);
           skType: begin
-            include(s.flags, sfUsed);
             if n.kind <> nkCall then
               liMessage(n.info, errXisNotCallable, s.name.s);
             // XXX does this check make any sense?
             result := semConv(c, n, s);
           end;
-          skProc, skConverter, skIterator: begin
+          skProc, skMethod, skConverter, skIterator: begin
             if s.magic = mNone then result := semDirectOp(c, n, flags)
             else result := semMagic(c, n, s, flags);
           end;
@@ -1328,7 +1322,8 @@ begin
     nkBracketExpr: begin
       checkMinSonsLen(n, 1);
       s := qualifiedLookup(c, n.sons[0], false);
-      if (s <> nil) and (s.kind in [skProc, skConverter, skIterator]) then begin
+      if (s <> nil)
+      and (s.kind in [skProc, skMethod, skConverter, skIterator]) then begin
         // type parameters: partial generic specialization
         // XXX: too implement!
         internalError(n.info, 'explicit generic instantation not implemented');
diff --git a/nim/semfold.pas b/nim/semfold.pas
index acca84c71..791f39149 100755
--- a/nim/semfold.pas
+++ b/nim/semfold.pas
@@ -288,7 +288,7 @@ begin
       result.typ := n.typ;
     end;
     mNewString, mExit, mInc, ast.mDec, mEcho, mAssert, mSwap,
-    mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq,
+    mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mSetLengthStr, mSetLengthSeq, mNLen..mNError: begin end;
     else InternalError(a.info, 'evalOp(' +{&} magicToStr[m] +{&} ')');
   end
@@ -412,7 +412,7 @@ begin
           else            result := copyTree(s.ast); // BUGFIX
         end
       end
-      else if s.kind = skProc then // BUGFIX
+      else if s.kind in [skProc, skMethod] then // BUGFIX
         result := n
     end;
     nkCharLit..nkNilLit: result := copyNode(n);
diff --git a/nim/seminst.pas b/nim/seminst.pas
index 6a6f272de..ea8889007 100755
--- a/nim/seminst.pas
+++ b/nim/seminst.pas
@@ -182,7 +182,7 @@ begin
     addDecl(c, result);
     if n.sons[codePos] <> nil then begin
       c.p := newProcCon(result);
-      if result.kind in [skProc, skConverter] then begin
+      if result.kind in [skProc, skMethod, skConverter] then begin
         addResult(c, result.typ.sons[0], n.info);
         addResultNode(c, n);
       end;
diff --git a/nim/semstmts.pas b/nim/semstmts.pas
index efbee6da8..b4b72a25b 100755
--- a/nim/semstmts.pas
+++ b/nim/semstmts.pas
@@ -316,7 +316,7 @@ var
 begin
   result := n;
   checkSonsLen(n, 1);
-  if not (c.p.owner.kind in [skConverter, skProc, skMacro]) then
+  if not (c.p.owner.kind in [skConverter, skMethod, skProc, skMacro]) then
     liMessage(n.info, errXNotAllowedHere, '''return''');
   if (n.sons[0] <> nil) then begin
     n.sons[0] := SemExprWithType(c, n.sons[0]);
@@ -970,11 +970,20 @@ begin
   result := semProcAux(c, n, skProc, procPragmas);
 end;
 
+function semMethod(c: PContext; n: PNode): PNode;
+begin
+  if not isTopLevel(c) then
+    liMessage(n.info, errXOnlyAtModuleScope, 'method');
+  result := semProcAux(c, n, skMethod, methodPragmas);
+end;
+
 function semConverterDef(c: PContext; n: PNode): PNode;
 var
   t: PType;
   s: PSym;
 begin
+  if not isTopLevel(c) then
+    liMessage(n.info, errXOnlyAtModuleScope, 'converter');
   checkSonsLen(n, codePos+1);
   if n.sons[genericParamsPos] <> nil then
     liMessage(n.info, errNoGenericParamsAllowedForX, 'converter');
@@ -1080,6 +1089,7 @@ begin
     nkPragma: pragma(c, c.p.owner, n, stmtPragmas);
     nkIteratorDef: result := semIterator(c, n);
     nkProcDef: result := semProc(c, n);
+    nkMethodDef: result := semMethod(c, n);
     nkConverterDef: result := semConverterDef(c, n);
     nkMacroDef: result := semMacroDef(c, n);
     nkTemplateDef: result := semTemplateDef(c, n);
diff --git a/nim/semtempl.pas b/nim/semtempl.pas
index f4173d9ff..8e37a1ea3 100755
--- a/nim/semtempl.pas
+++ b/nim/semtempl.pas
@@ -94,7 +94,7 @@ begin
     if i < a then 
       arg := n.sons[i]
     else 
-      arg := copyTree(s.ast.sons[paramsPos].sons[i-1].sym.ast);
+      arg := copyTree(s.typ.n.sons[i].sym.ast);
     if arg = nil then liMessage(n.info, errWrongNumberOfArguments);
     if not (s.typ.sons[i].kind in [tyTypeDesc, tyStmt, tyExpr]) then begin
       // concrete type means semantic checking for argument:
@@ -132,7 +132,7 @@ begin
   if i <= 1 then begin
     result := newSymNode(s);
     result.info := n.info;
-    include(s.flags, sfUsed);
+    markUsed(n, s);
   end
   else begin
     // semantic checking requires a type; ``fitNode`` deals with it
diff --git a/nim/semtypes.pas b/nim/semtypes.pas
index 934196de4..c0b86be86 100755
--- a/nim/semtypes.pas
+++ b/nim/semtypes.pas
@@ -232,7 +232,7 @@ function semTypeIdent(c: PContext; n: PNode): PSym;
 begin
   result := qualifiedLookup(c, n, true);
   if (result <> nil) then begin
-    include(result.flags, sfUsed);
+    markUsed(n, result);
     if result.kind <> skType then liMessage(n.info, errTypeExpected);
   end
   else
@@ -792,7 +792,7 @@ begin
           assignType(prev, t);
           result := prev;
         end;
-        include(n.sym.flags, sfUsed); // BUGFIX
+        markUsed(n, n.sym);
       end
       else
         liMessage(n.info, errTypeExpected);
diff --git a/nim/sigmatch.pas b/nim/sigmatch.pas
index aa53a741c..45a29fc29 100755
--- a/nim/sigmatch.pas
+++ b/nim/sigmatch.pas
@@ -103,7 +103,7 @@ begin
   candidates := '';
   sym := initOverloadIter(o, c, n.sons[0]);
   while sym <> nil do begin
-    if sym.kind in [skProc, skIterator, skConverter] then begin
+    if sym.kind in [skProc, skMethod, skIterator, skConverter] then begin
       add(candidates, getProcHeader(sym));
       add(candidates, nl)
     end;
@@ -709,7 +709,7 @@ begin
     best := -1;
     for i := 0 to sonsLen(arg)-1 do begin
       // iterators are not first class yet, so ignore them
-      if arg.sons[i].sym.kind in {@set}[skProc, skConverter] then begin
+      if arg.sons[i].sym.kind in {@set}[skProc, skMethod, skConverter] then begin
         copyCandidate(z, m);
         r := typeRel(z.bindings, f, arg.sons[i].typ);
         if r <> isNone then begin
@@ -734,7 +734,7 @@ begin
     end
     else begin
       // only one valid interpretation found:
-      include(arg.sons[best].sym.flags, sfUsed);
+      markUsed(arg, arg.sons[best].sym);
       result := ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best]);
     end
   end
@@ -884,6 +884,19 @@ begin
   end
 end;
 
+function sameMethodDispatcher(a, b: PSym): bool;
+var
+  aa, bb: PNode;
+begin
+  result := false;
+  if (a.kind = skMethod) and (b.kind = skMethod) then begin
+    aa := lastSon(a.ast);
+    bb := lastSon(b.ast);
+    if (aa.kind = nkSym) and (bb.kind = nkSym) and
+      (aa.sym = bb.sym) then result := true
+  end
+end;
+
 function semDirectCall(c: PContext; n: PNode; filter: TSymKinds): PNode;
 var
   sym: PSym;
@@ -923,7 +936,8 @@ begin
     // do not generate an error yet; the semantic checking will check for
     // an overloaded () operator
   end
-  else if (y.state = csMatch) and (cmpCandidates(x, y) = 0) then begin
+  else if (y.state = csMatch) and (cmpCandidates(x, y) = 0)
+  and not sameMethodDispatcher(x.calleeSym, y.calleeSym) then begin
     if x.state <> csMatch then
       InternalError(n.info, 'x.state is not csMatch');
     //writeMatches(x);
@@ -935,7 +949,7 @@ begin
   end
   else begin
     // only one valid interpretation found:
-    include(x.calleeSym.flags, sfUsed);
+    markUsed(n, x.calleeSym);
     if x.calleeSym.ast = nil then
       internalError(n.info, 'calleeSym.ast is nil'); // XXX: remove this check!
     if x.calleeSym.ast.sons[genericParamsPos] <> nil then begin
diff --git a/nim/transf.pas b/nim/transf.pas
index 79f65b754..15e885b40 100755
--- a/nim/transf.pas
+++ b/nim/transf.pas
@@ -15,7 +15,7 @@ unit transf;
 // * inlines constants
 // * performes contant folding
 // * introduces nkHiddenDeref, nkHiddenSubConv, etc.
-// * aggressive compile-time evaluation based on the side-effect analysis
+// * introduces method dispatchers
 
 interface
 
@@ -24,7 +24,7 @@ interface
 uses
   sysutils, nsystem, charsets, strutils,
   lists, options, ast, astalgo, trees, treetab, evals,
-  msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys;
+  msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys, cgmeth;
 
 const
   genPrefix = ':tmp'; // prefix for generated names
@@ -574,7 +574,7 @@ end;
 function getMagicOp(call: PNode): TMagic;
 begin
   if (call.sons[0].kind = nkSym)
-  and (call.sons[0].sym.kind in [skProc, skConverter]) then
+  and (call.sons[0].sym.kind in [skProc, skMethod, skConverter]) then
     result := call.sons[0].sym.magic
   else
     result := mNone
@@ -842,6 +842,11 @@ begin
     if sonsLen(result) = 2 then
       result := result.sons[1];
   end
+  else if (result.sons[0].kind = nkSym)
+  and (result.sons[0].sym.kind = skMethod) then begin
+    // use the dispatcher for the call:
+    result := methodCall(result);
+  end
   (*
   else if result.sons[0].kind = nkSym then begin
     // optimization still too aggressive
@@ -882,9 +887,12 @@ begin
     nkLambda: result := transformLambda(c, n);
     nkForStmt: result := transformFor(c, n);
     nkCaseStmt: result := transformCase(c, n);
-    nkProcDef, nkIteratorDef, nkMacroDef: begin
-      if n.sons[genericParamsPos] = nil then
+    nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef: begin
+      if n.sons[genericParamsPos] = nil then begin
         n.sons[codePos] := transform(c, n.sons[codePos]);
+        if n.kind = nkMethodDef then
+          methodDef(n.sons[namePos].sym);
+      end
     end;
     nkWhileStmt: begin
       if (sonsLen(n) <> 2) then InternalError(n.info, 'transform');
diff --git a/tests/tester.nim b/tests/tester.nim
index 03125bd4f..b09200aea 100755
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -23,7 +23,7 @@
 ## Nimrod shall produce.
 
 import
-  strutils, regexprs, os, osproc, streams
+  strutils, pegs, os, osproc, streams
 
 const
   cmdTemplate = r"nimrod cc --hints:on $options $filename"
@@ -38,7 +38,7 @@ type
 
 proc myExec(cmd: string): string =
   #echo("Executing: " & cmd)
-  result = osproc.executeProcess(cmd)
+  result = osproc.execProcess(cmd)
   #echo("Received: " & result)
 
 proc parseTest(filename: string): TSpec =
@@ -49,24 +49,30 @@ proc parseTest(filename: string): TSpec =
   result.file = filename
   for s in lines(filename):
     inc(i)
-    if find(s, r"\#OUT\s*(.*)", matches):
-      result.outp = matches[1]
+    if contains(s, peg"'#OUT' \s+ {.*}", matches):
+      result.outp = matches[0]
       break
-    if find(s, r"\#ERROR_IN\s*(\S*)\s*(\d+)", matches):
-      result.file = matches[1]
-      result.line = parseInt(matches[2])
+    if contains(s, peg"'#ERROR_IN' \s* {\S*} \s* {\d+}", matches):
+      result.file = matches[0]
+      result.line = parseInt(matches[1])
       result.err = true
       break
-    if find(s, r"\#ERROR_MSG\s*(.*)", matches):
+    if contains(s, peg"'#ERROR_MSG' \s* {.*}", matches):
       result.line = i
-      result.outp = matches[1]
+      result.outp = matches[0]
       result.err = True
       break
-    if find(s, r"\#ERROR$", matches):
+    if contains(s, peg"'#ERROR' \s* !.", matches):
       result.line = i
       result.err = true
       break
 
+var
+  pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
+  pegOtherError = peg"'Error:' \s* {.*}"
+  pegSuccess = peg"'Hint: operation successful'.*"
+  pegOfInterest = pegLineError / pegOtherError / pegSuccess
+
 proc callCompiler(filename, options: string): TSpec =
   var c = parseCmdLine(cmdTemplate % ["filename", filename, "options", options])
   var a: seq[string] = @[] # slicing is not yet implemented :-(
@@ -77,52 +83,50 @@ proc callCompiler(filename, options: string): TSpec =
   var s = ""
   while running(p) or not outp.atEnd(outp):
     var x = outp.readLine()
-    if match(x, r"(.*)\((\d+), \d+\) Error\: (.*)") or
-       match(x, r"Error\: (.*)") or
-       match(x, r"Hint\: operation successful"):
+    if x =~ pegOfInterest:
       # `s` should contain the last error message
       s = x
   result.outp = ""
   result.puremsg = ""
   result.file = ""
   result.err = true
-  if s =~ r"(.*)\((\d+), \d+\) Error\: (.*)":
-    result.file = matches[1]
-    result.line = parseInt(matches[2])
-    result.outp = matches[0]
-    result.puremsg = matches[3]
-  elif s =~ r"Error\: (.*)":
-    result.puremsg = matches[1]
-    result.outp = matches[0]
+  if s =~ pegLineError:
+    result.file = matches[0]
+    result.line = parseInt(matches[1])
+    result.outp = s
+    result.puremsg = matches[2]
+  elif s =~ pegOtherError:
+    result.puremsg = matches[0]
+    result.outp = s
     result.line = 1
-  elif s =~ r"Hint\: operation successful":
-    result.outp = matches[0]
+  elif s =~ pegSuccess:
+    result.outp = s
     result.err = false
 
-proc cmpResults(filename: string, spec, comp: TSpec): bool =
+proc sameResults(filename: string, spec, comp: TSpec): bool =
   # short filename for messages (better readability):
   var shortfile = os.extractFilename(filename)
 
   if comp.err and comp.outp == "":
     # the compiler did not say "[Error]" nor "operation sucessful"
-    Echo("[Tester] $1 -- FAILED; COMPILER BROKEN" % shortfile)
+    Echo("[Tester] $# -- FAILED; COMPILER BROKEN" % shortfile)
   elif spec.err != comp.err:
-    Echo(("[Tester] $1 -- FAILED\n" &
-         "Compiler says: $2\n" &
-         "But specification says: $3") %
+    Echo(("[Tester] $# -- FAILED\n" &
+         "Compiler says: $#\n" &
+         "But specification says: $#") %
          [shortfile, comp.outp, spec.outp])
   elif spec.err:
     if extractFilename(comp.file) != extractFilename(spec.file):
-      Echo(("[Tester] $1 -- FAILED: file names do not match:\n" &
-           "Compiler: $2\nSpec: $3") % [shortfile, comp.file, spec.file])
+      Echo(("[Tester] $# -- FAILED: file names do not match:\n" &
+           "Compiler: $#\nSpec: $#") % [shortfile, comp.file, spec.file])
     elif strip(spec.outp) notin strip(comp.puremsg):
-      Echo(("[Tester] $1 -- FAILED: error messages do not match:\n" &
-           "Compiler: $2\nSpec: $3") % [shortfile, comp.pureMsg, spec.outp])
+      Echo(("[Tester] $# -- FAILED: error messages do not match:\n" &
+           "Compiler: $#\nSpec: $#") % [shortfile, comp.pureMsg, spec.outp])
     elif comp.line != spec.line:
-      Echo(("[Tester] $1 -- FAILED: line numbers do not match:\n" &
-           "Compiler: $2\nSpec: $3") % [shortfile, $comp.line, $spec.line])
+      Echo(("[Tester] $# -- FAILED: line numbers do not match:\n" &
+           "Compiler: $#\nSpec: $#") % [shortfile, $comp.line, $spec.line])
     else:
-      Echo("[Tester] $1 -- OK" % shortfile)
+      Echo("[Tester] $# -- OK" % shortfile)
       result = true
   else:
     # we have to run the executable and check its output:
@@ -131,17 +135,17 @@ proc cmpResults(filename: string, spec, comp: TSpec): bool =
       if len(spec.outp) == 0:
         # we have no output to validate against, but compilation succeeded,
         # so it's okay:
-        Echo("[Tester] $1 -- OK" % shortfile)
+        Echo("[Tester] $# -- OK" % shortfile)
         result = true
       else:
         var buf = myExec(exeFile)
         result = strip(buf) == strip(spec.outp)
         if result:
-          Echo("[Tester] $1 -- compiled program OK" % shortfile)
+          Echo("[Tester] $# -- compiled program OK" % shortfile)
         else:
-          Echo("[Tester] $1 -- compiled program FAILED" % shortfile)
+          Echo("[Tester] $# -- compiled program FAILED" % shortfile)
     else:
-      Echo("[Tester] $1 -- FAILED; executable not found" % shortfile)
+      Echo("[Tester] $# -- FAILED; executable not found" % shortfile)
 
 proc main(options: string) =
   # runs the complete testsuite
@@ -151,16 +155,17 @@ proc main(options: string) =
     if extractFilename(filename) == "tester.nim": continue
     var spec = parseTest(filename)
     var comp = callCompiler(filename, options)
-    if cmpResults(filename, spec, comp): inc(passed)
+    if sameResults(filename, spec, comp): inc(passed)
     inc(total)
-  # ensure that the examples at least compile
+  # ensure that the examples at least compiles
   for filename in os.walkFiles("examples/*.nim"):
-    if executeShellCommand(cmdTemplate %
-                          ["filename", filename, "options", options]) == 0:
-      inc(passed)
-    else:
-      var shortfile = os.extractFilename(filename)
+    var comp = callCompiler(filename, options)
+    var shortfile = os.extractFilename(filename)
+    if comp.err:
       Echo("[Tester] Example '$#' -- FAILED" % shortfile)
+    else:
+      Echo("[Tester] Example $# -- OK" % shortfile)
+      inc(passed)
     inc(total)
   Echo("[Tester] $#/$# tests passed\n" % [$passed, $total])
 
diff --git a/tools/inno.tmpl b/tools/inno.tmpl
index 3cfc04f2d..368846d66 100755
--- a/tools/inno.tmpl
+++ b/tools/inno.tmpl
@@ -20,7 +20,7 @@ Name: english; MessagesFile: compiler:Default.isl
 [Files] 
   #for i in low(TFileCategory)..fcWindows:
   #  for f in items(c.cat[i]):
-Source: ${expandFilename(f)}; DestDir: {app}\${extractDir(f)}; Flags: ignoreversion
+Source: ${expandFilename(f)}; DestDir: {app}\${splitFile(f).dir}; Flags: ignoreversion
   #  end for
   #end for
 
diff --git a/tools/install.tmpl b/tools/install.tmpl
index ffefc8281..acca8a7fb 100755
--- a/tools/install.tmpl
+++ b/tools/install.tmpl
@@ -58,7 +58,7 @@ if [ $# -eq 1 ] ; then
 #var createdDirs = newStringTable()
 #for cat in fcConfig..fcLib:
 #  for f in items(c.cat[cat]):
-#    var mk = extractDir(f.skipRoot)
+#    var mk = splitFile(f.skipRoot).dir
 #    if mk.len > 0:
 #      mk = unixDirVars[cat] & "/" & mk
 #      if not createdDirs.hasKey(mk):
diff --git a/tools/niminst.nim b/tools/niminst.nim
index 13024b68f..b1de0964c 100755
--- a/tools/niminst.nim
+++ b/tools/niminst.nim
@@ -116,7 +116,7 @@ Compile_options:
 """
 
 proc parseCmdLine(c: var TConfigData) =
-  var p = init()
+  var p = initOptParser()
   while true:
     next(p)
     var kind = p.kind
@@ -132,8 +132,8 @@ proc parseCmdLine(c: var TConfigData) =
           of "inno": incl(c.actions, actionInno)
           else: quit(Usage)
       else:
-        c.infile = appendFileExt(key, "ini")
-        c.nimrodArgs = getRestOfCommandLine(p)
+        c.infile = addFileExt(key, "ini")
+        c.nimrodArgs = cmdLineRest(p)
         break
     of cmdLongOption, cmdShortOption:
       case normalize(key)
@@ -255,7 +255,7 @@ proc parseIniFile(c: var TConfigData) =
 proc readCFiles(c: var TConfigData, osA, cpuA: int) =
   var cfg: TCfgParser
   var cfilesSection = false
-  var f = extractDir(c.infile) / "mapping.txt"
+  var f = splitFile(c.infile).dir / "mapping.txt"
   c.cfiles[osA][cpuA] = @[]
   var input = newFileStream(f, fmRead)
   if input != nil:
@@ -302,7 +302,7 @@ proc srcdist(c: var TConfigData) =
                  [c.oses[osA-1], c.cpus[cpuA-1], c.nimrodArgs, 
                  changeFileExt(c.infile, "nim")]
       echo(cmd)
-      if executeShellCommand(cmd) != 0:
+      if execShellCmd(cmd) != 0:
         quit("Error: call to nimrod compiler failed")
       readCFiles(c, osA, cpuA)
       for i in 0 .. c.cfiles[osA][cpuA].len-1:
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index 5ba5799f9..135ba518a 100755
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -56,7 +56,7 @@ Compile_options:
 """
 
 proc parseCmdLine(c: var TConfigData) =
-  var p = init()
+  var p = initOptParser()
   while true:
     next(p)
     var kind = p.kind
@@ -64,8 +64,8 @@ proc parseCmdLine(c: var TConfigData) =
     var val = p.val
     case kind
     of cmdArgument:
-      c.infile = appendFileExt(key, "ini")
-      c.nimrodArgs = getRestOfCommandLine(p)
+      c.infile = addFileExt(key, "ini")
+      c.nimrodArgs = cmdLineRest(p)
       break
     of cmdLongOption, cmdShortOption:
       case normalize(key)
@@ -84,7 +84,7 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) =
   for k, f in walkDir(root):
     case k
     of pcFile, pcLinkToFile:
-      if cmpIgnoreCase(ext, extractFileExt(f)) == 0:
+      if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
         add(s, f)
     of pcDirectory: walkDirRecursively(s, f, ext)
     of pcLinkToDirectory: nil
@@ -94,7 +94,7 @@ proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
     if existsDir(dir / p):
       walkDirRecursively(s, dir / p, ext)
     else:
-      add(s, dir / appendFileExt(p, ext))
+      add(s, dir / addFileExt(p, ext))
 
 proc parseIniFile(c: var TConfigData) =
   var
@@ -143,7 +143,7 @@ proc parseIniFile(c: var TConfigData) =
     if c.projectName.len == 0:
       c.projectName = changeFileExt(extractFilename(c.infile), "")
     if c.outdir.len == 0:
-      c.outdir = extractDir(c.infile)
+      c.outdir = splitFile(c.infile).dir
   else:
     quit("cannot open: " & c.infile)
 
@@ -151,18 +151,18 @@ proc parseIniFile(c: var TConfigData) =
 
 proc Exec(cmd: string) =
   echo(cmd)
-  if os.executeShellCommand(cmd) != 0: quit("external program failed")
+  if os.execShellCmd(cmd) != 0: quit("external program failed")
 
 proc buildDoc(c: var TConfigData, destPath: string) =
   # call nim for the documentation:
   for d in items(c.doc):
     Exec("nimrod rst2html $# -o:$# --index=$#/theindex $#" %
-      [c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"),
+      [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"),
        destpath, d])
     Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d])
   for d in items(c.srcdoc):
     Exec("nimrod doc $# -o:$# --index=$#/theindex $#" %
-      [c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"),
+      [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"),
        destpath, d])
   Exec("nimrod rst2html $1 -o:$2/theindex.html $2/theindex" %
        [c.nimrodArgs, destPath])
@@ -171,7 +171,7 @@ proc buildAddDoc(c: var TConfigData, destPath: string) =
   # build additional documentation (without the index):
   for d in items(c.webdoc):
     Exec("nimrod doc $# -o:$# $#" %
-      [c.nimrodArgs, destPath / changeFileExt(extractFileTrunk(d), "html"), d])
+      [c.nimrodArgs, destPath / changeFileExt(splitFile(d).name, "html"), d])
 
 proc main(c: var TConfigData) =
   const
diff --git a/tools/trimcc.nim b/tools/trimcc.nim
index f946256c9..966f406d4 100755
--- a/tools/trimcc.nim
+++ b/tools/trimcc.nim
@@ -2,8 +2,9 @@
 

 import strutils, os

 

-proc newName(f: string): string = 

-  return extractDir(f) / "trim_" & extractFilename(f)

+proc newName(f: string): string =

+  var (dir, name, ext) = splitFile(f)

+  return dir / "trim_" & name & ext

 

 proc walker(dir: string) = 

   for kind, path in walkDir(dir):

@@ -11,7 +12,7 @@ proc walker(dir: string) =
     of pcFile:

       moveFile(newName(path), path)

       # test if installation still works:

-      if executeShellCommand(r"nimrod c --force_build tests\tlastmod") == 0:

+      if execShellCmd(r"nimrod c --force_build tests\tlastmod") == 0:

         echo "Optional: ", path

         removeFile(newName(path))

       else:

diff --git a/web/index.txt b/web/index.txt
index 5bc48801d..e0ca80eb3 100755
--- a/web/index.txt
+++ b/web/index.txt
@@ -52,6 +52,7 @@ Nimrod is efficient
   from pointers to manually managed memory.
 * Zero-overhead iterators.
 * Cross-module inlining.
+* Dynamic method binding with inlining and without virtual method table.
 * Compile time evaluation of user-defined functions.
 * Whole program dead code elimination: Only *used functions* are included in 
   the executable.
@@ -78,10 +79,8 @@ Nimrod is elegant
   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**.
-* Nimrod is a simple language that leads to simple programs. However,
-  the language is not crippled in the name of simplicity.
+  Indentation must not contain tabulators so the compiler always sees
+  the code the same way as you do.
 
 
 Nimrod plays nice with others
@@ -97,11 +96,10 @@ Nimrod plays nice with others
 * Nimrod's documentation syntax is a subset of the wonderfully readable plaintext 
   markup syntax 
   `reStructuredText <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_.
-  **The documentation generator is very flexible; this website has been generated
-  with it!**
+  The documentation generator is very flexible; this website has been generated
+  with it!
   
 
-
 Roadmap to 1.0
 ==============
 
diff --git a/web/news.txt b/web/news.txt
index 0896023dc..65b3a5b3c 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -21,7 +21,7 @@ Additions
 ---------
 
 - implemented generic types and two phase symbol lookup in generic routines 
-- template parameters can now be real types
+- template parameters can now have real types
 - implemented generalized raw string literals: ``ident"abc"`` is a shortcut for
   ``ident(r"abc")``
 - in overloading resolution iterators are separated from procs; iterators now
diff --git a/web/nimrod.ini b/web/nimrod.ini
index 4bb611c71..1f58a0915 100755
--- a/web/nimrod.ini
+++ b/web/nimrod.ini
@@ -21,11 +21,11 @@ FAQ: question
 file: ticker
 
 [Documentation]
-doc: "endb;intern;lib;manual;tut1;tut2;nimrodc;overview"
+doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
 srcdoc: "system.nim;pure/os;pure/strutils;pure/regexprs;pure/math"
-srcdoc: "pure/complex;pure/times;pure/osproc"
+srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
 srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase"
-srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv"
+srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql"
 srcdoc: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode"
 srcdoc: "impure/zipfiles;pure/xmlgen;pure/macros"