summary refs log tree commit diff stats
path: root/lib/impure
diff options
context:
space:
mode:
authorjlp765 <jlp765@gmail.com>2017-01-17 00:55:15 +1000
committerAndreas Rumpf <rumpf_a@web.de>2017-01-16 15:55:15 +0100
commit704eae84951df114f5aa9cc40fdecb2ad788ea16 (patch)
treecac43edd7a83d2fd1db5ce055a1bc824c9205eb5 /lib/impure
parent108f5e688ed85768e2477b47cc2e7743331ad5cd (diff)
downloadNim-704eae84951df114f5aa9cc40fdecb2ad788ea16.tar.gz
re additions for buffer (cstring) RE matching (#5117)
* Replace expr with untyped

* Add buffer (cstring) related procs
Replace expr with untyped
Replace testing assert() procs with doAssert()

* make the string variants call the cstring variants in order to fight code size

* Remove redundant proc

* fix casting of cstring
add init of variables (identified by verbosity:3)

* Speed up - use pattern.e for exec()
inline some procs
Diffstat (limited to 'lib/impure')
-rw-r--r--lib/impure/re.nim342
1 files changed, 244 insertions, 98 deletions
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index f7f7c5b25..9d5d2bdd0 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -15,8 +15,7 @@
 ##
 ## **Note:** The 're' proc defaults to the **extended regular expression
 ## syntax** which lets you use whitespace freely to make your regexes readable.
-## However, this means to match whitespace ``\s`` or something similar has
-## to be used.
+## However, this means matching whitespace requires ``\s`` or something similar.
 ##
 ## This module is implemented by providing a wrapper around the
 ## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
@@ -65,8 +64,8 @@ proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
 
 proc rawCompile(pattern: string, flags: cint): ptr Pcre =
   var
-    msg: cstring
-    offset: cint
+    msg: cstring = ""
+    offset: cint = 0
   result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil)
   if result == nil:
     raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
@@ -80,66 +79,87 @@ proc finalizeRegEx(x: Regex) =
     pcre.free_substring(cast[cstring](x.e))
 
 proc re*(s: string, flags = {reExtended, reStudy}): Regex =
-  ## Constructor of regular expressions. Note that Nim's
-  ## extended raw string literals support this syntax ``re"[abc]"`` as
+  ## Constructor of regular expressions.
+  ##
+  ## Note that Nim's
+  ## extended raw string literals support the syntax ``re"[abc]"`` as
   ## a short form for ``re(r"[abc]")``.
   new(result, finalizeRegEx)
   result.h = rawCompile(s, cast[cint](flags - {reStudy}))
   if reStudy in flags:
-    var msg: cstring
+    var msg: cstring = ""
     var options: cint = 0
-    var hasJit: cint
+    var hasJit: cint = 0
     if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
       if hasJit == 1'i32:
         options = pcre.STUDY_JIT_COMPILE
     result.e = pcre.study(result.h, options, addr msg)
     if not isNil(msg): raiseInvalidRegex($msg)
 
-proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
-                 start, flags: cint): cint =
+proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} =
+  ## Return a Nim string built from a slice of a cstring buffer.
+  ## Don't assume cstring is '\0' terminated
+  let sz = ePos - sPos
+  result = newString(sz+1)
+  copyMem(addr(result[0]), unsafeaddr(b[sPos]), sz)
+  result.setLen(sz)
+
+proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
+                 start, bufSize, flags: cint): cint =
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start, flags,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize, start, flags,
       cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return res
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32:
+      matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = nil
   return rawMatches[1] - rawMatches[0]
 
-proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
-                 start = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of `pattern` in `s`
+proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the starting position and end position of ``pattern`` in ``buf``
+  ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated),
   ## and the captured
-  ## substrings in the array `matches`. If it does not match, nothing
-  ## is written into `matches` and ``(-1,0)`` is returned.
+  ## substrings in the array ``matches``. If it does not match, nothing
+  ## is written into ``matches`` and ``(-1,0)`` is returned.
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
       cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return (-1, 0)
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = nil
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
-proc findBounds*(s: string, pattern: Regex,
-                 matches: var openArray[tuple[first, last: int]],
-                 start = 0): tuple[first, last: int] =
+proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
+                 start = 0): tuple[first, last: int] {.inline.} =
   ## returns the starting position and end position of ``pattern`` in ``s``
-  ## and the captured substrings in the array `matches`.
-  ## If it does not match, nothing is written into `matches` and
+  ## and the captured substrings in the array ``matches``.
+  ## If it does not match, nothing
+  ## is written into ``matches`` and ``(-1,0)`` is returned.
+  result = findBounds(cstring(s), pattern, matches, start, s.len)
+
+proc findBounds*(buf: cstring, pattern: Regex,
+                 matches: var openArray[tuple[first, last: int]],
+                 start = 0, bufSize = 0): tuple[first, last: int] =
+  ## returns the starting position and end position of ``pattern`` in ``buf``
+  ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated),
+  ## and the captured substrings in the array ``matches``.
+  ## If it does not match, nothing is written into ``matches`` and
   ## ``(-1,0)`` is returned.
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
       cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return (-1, 0)
   for i in 1..int(res)-1:
@@ -150,80 +170,161 @@ proc findBounds*(s: string, pattern: Regex,
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
 proc findBounds*(s: string, pattern: Regex,
-                 start = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of ``pattern`` in ``s``.
+                 matches: var openArray[tuple[first, last: int]],
+                 start = 0): tuple[first, last: int] {.inline.} =
+  ## returns the starting position and end position of ``pattern`` in ``s``
+  ## and the captured substrings in the array ``matches``.
+  ## If it does not match, nothing is written into ``matches`` and
+  ## ``(-1,0)`` is returned.
+  result = findBounds(cstring(s), pattern, matches, start, s.len)
+
+proc findBounds*(buf: cstring, pattern: Regex,
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the ``first`` and ``last`` position of ``pattern`` in ``buf``,
+  ## where ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
   ## If it does not match, ``(-1,0)`` is returned.
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
       cast[ptr cint](rawMatches), 3)
   if res < 0'i32: return (int(res), 0)
   return (int(rawMatches[0]), int(rawMatches[1]-1))
 
-proc matchOrFind(s: string, pattern: Regex, start, flags: cint): cint =
+proc findBounds*(s: string, pattern: Regex,
+                 start = 0): tuple[first, last: int] {.inline.} =
+  ## returns the ``first`` and ``last`` position of ``pattern`` in ``s``.
+  ## If it does not match, ``(-1,0)`` is returned.
+  ##
+  ## Note: there is a speed improvement if the matches do not need to be captured.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   assert findBounds("01234abc89", re"abc") == (5,7)
+  result = findBounds(cstring(s), pattern, start, s.len)
+
+proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint): cint =
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
-  result = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start, flags,
+  result = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags,
                     cast[ptr cint](rawMatches), 3)
   if result >= 0'i32:
     result = rawMatches[1] - rawMatches[0]
 
 proc matchLen*(s: string, pattern: Regex, matches: var openArray[string],
-              start = 0): int =
+              start = 0): int {.inline.} =
   ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, -1 is returned. Note that a match length
+  ## if there is no match, ``-1`` is returned. Note that a match length
   ## of zero can happen.
-  return matchOrFind(s, pattern, matches, start.cint, pcre.ANCHORED)
+  result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED)
 
-proc matchLen*(s: string, pattern: Regex, start = 0): int =
+proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string],
+              start = 0, bufSize: int): int {.inline.} =
   ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, -1 is returned. Note that a match length
+  ## if there is no match, ``-1`` is returned. Note that a match length
   ## of zero can happen.
-  return matchOrFind(s, pattern, start.cint, pcre.ANCHORED)
+  return matchOrFind(buf, pattern, matches, start.cint, bufSize.cint, pcre.ANCHORED)
+
+proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## of zero can happen.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   echo matchLen("abcdefg", re"cde", 2)  # =>  3
+  ##   echo matchLen("abcdefg", re"abcde")   # =>  5
+  ##   echo matchLen("abcdefg", re"cde")     # => -1
+  result = matchOrFind(cstring(s), pattern, start.cint, s.len.cint, pcre.ANCHORED)
 
-proc match*(s: string, pattern: Regex, start = 0): bool =
+proc matchLen*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int {.inline.} =
+  ## the same as ``match``, but it returns the length of the match,
+  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## of zero can happen.
+  result = matchOrFind(buf, pattern, start.cint, bufSize, pcre.ANCHORED)
+
+proc match*(s: string, pattern: Regex, start = 0): bool {.inline.} =
   ## returns ``true`` if ``s[start..]`` matches the ``pattern``.
-  result = matchLen(s, pattern, start) != -1
+  result = matchLen(cstring(s), pattern, start, s.len) != -1
 
 proc match*(s: string, pattern: Regex, matches: var openArray[string],
-           start = 0): bool =
+           start = 0): bool {.inline.} =
   ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
   ## the captured substrings in the array ``matches``. If it does not
   ## match, nothing is written into ``matches`` and ``false`` is
   ## returned.
-  result = matchLen(s, pattern, matches, start) != -1
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   var matches: array[2, string]
+  ##   if match("abcdefg", re"c(d)ef(g)", matches, 2):
+  ##     for s in matches:
+  ##       echo s       # => d g
+  result = matchLen(cstring(s), pattern, matches, start, s.len) != -1
+
+proc match*(buf: cstring, pattern: Regex, matches: var openArray[string],
+           start = 0, bufSize: int): bool {.inline.} =
+  ## returns ``true`` if ``buf[start..<bufSize]`` matches the ``pattern`` and
+  ## the captured substrings in the array ``matches``. If it does not
+  ## match, nothing is written into ``matches`` and ``false`` is
+  ## returned.
+  ## ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
+  result = matchLen(buf, pattern, matches, start, bufSize) != -1
 
-proc find*(s: string, pattern: Regex, matches: var openArray[string],
-           start = 0): int =
-  ## returns the starting position of ``pattern`` in ``s`` and the captured
+proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
+           start = 0, bufSize = 0): int =
+  ## returns the starting position of ``pattern`` in ``buf`` and the captured
   ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and -1 is returned.
+  ## is written into ``matches`` and ``-1`` is returned.
+  ## ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
       cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return res
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)-1)
     else: matches[i-1] = nil
   return rawMatches[0]
 
-proc find*(s: string, pattern: Regex, start = 0): int =
-  ## returns the starting position of ``pattern`` in ``s``. If it does not
-  ## match, -1 is returned.
+proc find*(s: string, pattern: Regex, matches: var openArray[string],
+           start = 0): int {.inline.} =
+  ## returns the starting position of ``pattern`` in ``s`` and the captured
+  ## substrings in the array ``matches``. If it does not match, nothing
+  ## is written into ``matches`` and ``-1`` is returned.
+  result = find(cstring(s), pattern, matches, start, s.len)
+
+proc find*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int =
+  ## returns the starting position of ``pattern`` in ``buf``,
+  ## where ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
+  ## If it does not match, ``-1`` is returned.
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
-    res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
       cast[ptr cint](rawMatches), 3)
   if res < 0'i32: return res
   return rawMatches[0]
 
+proc find*(s: string, pattern: Regex, start = 0): int {.inline.} =
+  ## returns the starting position of ``pattern`` in ``s``. If it does not
+  ## match, ``-1`` is returned.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##  echo find("abcdefg", re"cde")  # => 2
+  ##  echo find("abcdefg", re"abc")  # => 0
+  ##  echo find("abcdefg", re"zz")  # => -1
+  result = find(cstring(s), pattern, start, s.len)
+
 iterator findAll*(s: string, pattern: Regex, start = 0): string =
   ## Yields all matching *substrings* of `s` that match `pattern`.
   ##
@@ -243,15 +344,36 @@ iterator findAll*(s: string, pattern: Regex, start = 0): string =
     yield substr(s, int(a), int(b)-1)
     i = b
 
-proc findAll*(s: string, pattern: Regex, start = 0): seq[string] =
-  ## returns all matching *substrings* of `s` that match `pattern`.
+iterator findAll*(buf: cstring, pattern: Regex, start = 0, bufSize: int): string =
+  ## Yields all matching `substrings` of ``s`` that match ``pattern``.
+  ##
+  ## Note that since this is an iterator you should not modify the string you
+  ## are iterating over: bad things could happen.
+  var
+    i = int32(start)
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
+  while true:
+    let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, i, 0'i32,
+      cast[ptr cint](rawMatches), 3)
+    if res < 0'i32: break
+    let a = rawMatches[0]
+    let b = rawMatches[1]
+    if a == b and a == i: break
+    var str = newString(b-a)
+    copyMem(str[0].addr, unsafeAddr(buf[a]), b-a)
+    yield str
+    i = b
+
+proc findAll*(s: string, pattern: Regex, start = 0): seq[string] {.inline.} =
+  ## returns all matching `substrings` of ``s`` that match ``pattern``.
   ## If it does not match, @[] is returned.
   accumulateResult(findAll(s, pattern, start))
 
 when not defined(nimhygiene):
   {.pragma: inject.}
 
-template `=~` *(s: string, pattern: Regex): expr =
+template `=~` *(s: string, pattern: Regex): untyped =
   ## This calls ``match`` with an implicit declared ``matches`` array that
   ## can be used in the scope of the ``=~`` call:
   ##
@@ -276,27 +398,29 @@ template `=~` *(s: string, pattern: Regex): expr =
 
 # ------------------------- more string handling ------------------------------
 
-proc contains*(s: string, pattern: Regex, start = 0): bool =
+proc contains*(s: string, pattern: Regex, start = 0): bool {.inline.} =
   ## same as ``find(s, pattern, start) >= 0``
   return find(s, pattern, start) >= 0
 
 proc contains*(s: string, pattern: Regex, matches: var openArray[string],
-              start = 0): bool =
+              start = 0): bool {.inline.} =
   ## same as ``find(s, pattern, matches, start) >= 0``
   return find(s, pattern, matches, start) >= 0
 
-proc startsWith*(s: string, prefix: Regex): bool =
+proc startsWith*(s: string, prefix: Regex): bool {.inline.} =
   ## returns true if `s` starts with the pattern `prefix`
   result = matchLen(s, prefix) >= 0
 
-proc endsWith*(s: string, suffix: Regex): bool =
+proc endsWith*(s: string, suffix: Regex): bool {.inline.} =
   ## returns true if `s` ends with the pattern `prefix`
   for i in 0 .. s.len-1:
     if matchLen(s, suffix, i) == s.len - i: return true
 
 proc replace*(s: string, sub: Regex, by = ""): string =
-  ## Replaces `sub` in `s` by the string `by`. Captures cannot be
-  ## accessed in `by`. Examples:
+  ## Replaces ``sub`` in ``s`` by the string ``by``. Captures cannot be
+  ## accessed in ``by``.
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   "var1=key; var2=key2".replace(re"(\w+)=(\w+)")
@@ -317,8 +441,10 @@ proc replace*(s: string, sub: Regex, by = ""): string =
   add(result, substr(s, prev))
 
 proc replacef*(s: string, sub: Regex, by: string): string =
-  ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
-  ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
+  ## Replaces ``sub`` in ``s`` by the string ``by``. Captures can be accessed in ``by``
+  ## with the notation ``$i`` and ``$#`` (see strutils.\`%\`).
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2")
@@ -343,7 +469,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
 
 proc parallelReplace*(s: string, subs: openArray[
                       tuple[pattern: Regex, repl: string]]): string =
-  ## Returns a modified copy of `s` with the substitutions in `subs`
+  ## Returns a modified copy of ``s`` with the substitutions in ``subs``
   ## applied in parallel.
   result = ""
   var i = 0
@@ -363,17 +489,19 @@ proc parallelReplace*(s: string, subs: openArray[
 
 proc transformFile*(infile, outfile: string,
                     subs: openArray[tuple[pattern: Regex, repl: string]]) =
-  ## reads in the file `infile`, performs a parallel replacement (calls
-  ## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
+  ## reads in the file ``infile``, performs a parallel replacement (calls
+  ## ``parallelReplace``) and writes back to ``outfile``. Raises ``IOError`` if an
   ## error occurs. This is supposed to be used for quick scripting.
   var x = readFile(infile).string
   writeFile(outfile, x.parallelReplace(subs))
 
 iterator split*(s: string, sep: Regex): string =
-  ## Splits the string `s` into substrings.
+  ## Splits the string ``s`` into substrings.
+  ##
+  ## Substrings are separated by the regular expression ``sep``
+  ## (and the portion matched by ``sep`` is not returned).
   ##
-  ## Substrings are separated by the regular expression `sep`.
-  ## Examples:
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   for word in split("00232this02939is39an22example111", re"\d+"):
@@ -404,12 +532,14 @@ iterator split*(s: string, sep: Regex): string =
     if first <= last:
       yield substr(s, first, last-1)
 
-proc split*(s: string, sep: Regex): seq[string] =
-  ## Splits the string `s` into substrings.
+proc split*(s: string, sep: Regex): seq[string] {.inline.} =
+  ## Splits the string ``s`` into a seq of substrings.
+  ##
+  ## The portion matched by ``sep`` is not returned.
   accumulateResult(split(s, sep))
 
 proc escapeRe*(s: string): string =
-  ## escapes `s` so that it is matched verbatim when used as a regular
+  ## escapes ``s`` so that it is matched verbatim when used as a regular
   ## expression.
   result = ""
   for c in items(s):
@@ -446,70 +576,86 @@ const ## common regular expressions
     ## describes an URL
 
 when isMainModule:
-  assert match("(a b c)", re"\( .* \)")
-  assert match("WHiLe", re("while", {reIgnoreCase}))
+  doAssert match("(a b c)", re"\( .* \)")
+  doAssert match("WHiLe", re("while", {reIgnoreCase}))
 
-  assert "0158787".match(re"\d+")
-  assert "ABC 0232".match(re"\w+\s+\d+")
-  assert "ABC".match(re"\d+ | \w+")
+  doAssert "0158787".match(re"\d+")
+  doAssert "ABC 0232".match(re"\w+\s+\d+")
+  doAssert "ABC".match(re"\d+ | \w+")
 
-  assert matchLen("key", re(reIdentifier)) == 3
+  {.push warnings:off.}
+  doAssert matchLen("key", re(reIdentifier)) == 3
+  {.pop.}
 
   var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
-  assert matchLen("key1=  cal9", pattern) == 11
+  doAssert matchLen("key1=  cal9", pattern) == 11
 
-  assert find("_____abc_______", re"abc") == 5
+  doAssert find("_____abc_______", re"abc") == 5
+  doAssert findBounds("_____abc_______", re"abc") == (5,7)
 
   var matches: array[6, string]
   if match("abcdefg", re"c(d)ef(g)", matches, 2):
-    assert matches[0] == "d"
-    assert matches[1] == "g"
+    doAssert matches[0] == "d"
+    doAssert matches[1] == "g"
   else:
-    assert false
+    doAssert false
 
   if "abc" =~ re"(a)bcxyz|(\w+)":
-    assert matches[1] == "abc"
+    doAssert matches[1] == "abc"
   else:
-    assert false
+    doAssert false
 
   if "abc" =~ re"(cba)?.*":
-    assert matches[0] == nil
-  else: assert false
+    doAssert matches[0] == nil
+  else: doAssert false
 
   if "abc" =~ re"().*":
-    assert matches[0] == ""
-  else: assert false
+    doAssert matches[0] == ""
+  else: doAssert false
 
-  assert "var1=key; var2=key2".endsWith(re"\w+=\w+")
-  assert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
+  doAssert "var1=key; var2=key2".endsWith(re"\w+=\w+")
+  doAssert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
          "var1<-keykey; var2<-key2key2")
-  assert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
+  doAssert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
          "$1<-$2$2; $1<-$2$2")
 
   var accum: seq[string] = @[]
   for word in split("00232this02939is39an22example111", re"\d+"):
     accum.add(word)
-  assert(accum == @["", "this", "is", "an", "example", ""])
+  doAssert(accum == @["", "this", "is", "an", "example", ""])
 
   accum = @[]
   for word in split("AAA :   : BBB", re"\s*:\s*"):
     accum.add(word)
-  assert(accum == @["AAA", "", "BBB"])
+  doAssert(accum == @["AAA", "", "BBB"])
 
   for x in findAll("abcdef", re"^{.}", 3):
-    assert x == "d"
+    doAssert x == "d"
   accum = @[]
   for x in findAll("abcdef", re".", 3):
     accum.add(x)
-  assert(accum == @["d", "e", "f"])
+  doAssert(accum == @["d", "e", "f"])
 
-  assert("XYZ".find(re"^\d*") == 0)
-  assert("XYZ".match(re"^\d*") == true)
+  doAssert("XYZ".find(re"^\d*") == 0)
+  doAssert("XYZ".match(re"^\d*") == true)
 
   block:
     var matches: array[16, string]
     if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches):
       for i in 0..matches.high:
-        assert matches[i] == $chr(i + 'a'.ord)
+        doAssert matches[i] == $chr(i + 'a'.ord)
     else:
-      assert false
+      doAssert false
+
+  block:   # Buffer based RE
+    var cs: cstring = "_____abc_______"
+    doAssert(cs.find(re"abc", bufSize=15) == 5)
+    doAssert(cs.matchLen(re"_*abc", bufSize=15) == 8)
+    doAssert(cs.matchLen(re"abc", start=5, bufSize=15) == 3)
+    doAssert(cs.matchLen(re"abc", start=5, bufSize=7) == -1)
+    doAssert(cs.matchLen(re"abc_*", start=5, bufSize=10) == 5)
+    var accum: seq[string] = @[]
+    for x in cs.findAll(re"[a-z]", start=3, bufSize=15):
+      accum.add($x)
+    doAssert(accum == @["a","b","c"])
+