summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/parseutils.nim536
-rw-r--r--lib/system.nim14
-rw-r--r--lib/system/jssys.nim21
-rw-r--r--lib/system/strmantle.nim24
4 files changed, 481 insertions, 114 deletions
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 1e3c2f72c..2bb23c626 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -50,6 +50,8 @@
 
 include "system/inclrtl"
 
+template toOa(s: string): openArray[char] = openArray[char](s)
+
 const
   Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
@@ -59,8 +61,7 @@ const
 proc toLower(c: char): char {.inline.} =
   result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
 
-proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses a binary number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -89,7 +90,7 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
     doAssert num64 == 336784608873
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -104,10 +105,9 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses an octal number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -136,7 +136,7 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseOct("2346475523464755", num64) == 16
     doAssert num64 == 86216859871725
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -151,10 +151,9 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses a hexadecimal number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -184,7 +183,7 @@ proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseHex("4E69ED4E69ED", num64) == 12
     doAssert num64 == 86216859871725
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -206,9 +205,9 @@ proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseIdent*(s: string, ident: var string, start = 0): int =
+proc parseIdent*(s: openArray[char], ident: var string): int =
   ## Parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
   ## If error, the value of `ident` is not changed.
@@ -220,14 +219,14 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     doAssert res == "ello"
     doAssert parseIdent("Hello World", res, 6) == 5
     doAssert res == "World"
-  var i = start
+  var i = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    ident = substr(s, start, i-1)
-    result = i-start
+    ident = substr(s.toOpenArray(0, i-1))
+    result = i
 
-proc parseIdent*(s: string, start = 0): string =
+proc parseIdent*(s: openArray[char]): string =
   ## Parses an identifier and returns it or an empty string in
   ## case of an error.
   runnableExamples:
@@ -236,13 +235,13 @@ proc parseIdent*(s: string, start = 0): string =
     doAssert parseIdent("Hello World", 5) == ""
     doAssert parseIdent("Hello World", 6) == "World"
   result = ""
-  var i = start
+  var i = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    result = substr(s, start, i-1)
+    result = substr(s.toOpenArray(0, i - 1))
 
-proc parseChar*(s: string, c: var char, start = 0): int =
+proc parseChar*(s: openArray[char], c: var char): int =
   ## Parses a single character, stores it in `c` and returns 1.
   ## In case of error (if start >= s.len) it returns 0
   ## and the value of `c` is unchanged.
@@ -252,11 +251,11 @@ proc parseChar*(s: string, c: var char, start = 0): int =
     doAssert c == '\0'
     doAssert "nim".parseChar(c, 0) == 1
     doAssert c == 'n'
-  if start < s.len:
-    c = s[start]
+  if s.len > 0:
+    c = s[0]
     result = 1
 
-proc skipWhitespace*(s: string, start = 0): int {.inline.} =
+proc skipWhitespace*(s: openArray[char]): int {.inline.} =
   ## Skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
   runnableExamples:
@@ -265,9 +264,9 @@ proc skipWhitespace*(s: string, start = 0): int {.inline.} =
     doAssert skipWhitespace("Hello World", 5) == 1
     doAssert skipWhitespace("Hello  World", 5) == 2
   result = 0
-  while start+result < s.len and s[start+result] in Whitespace: inc(result)
+  while result < s.len and s[result] in Whitespace: inc(result)
 
-proc skip*(s, token: string, start = 0): int {.inline.} =
+proc skip*(s, token: openArray[char]): int {.inline.} =
   ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
   runnableExamples:
@@ -277,22 +276,22 @@ proc skip*(s, token: string, start = 0): int {.inline.} =
     doAssert skip("CAPlow", "CAP", 0) == 3
     doAssert skip("CAPlow", "cap", 0) == 0
   result = 0
-  while start+result < s.len and result < token.len and
-      s[result+start] == token[result]:
+  while result < s.len and result < token.len and
+      s[result] == token[result]:
     inc(result)
   if result != token.len: result = 0
 
-proc skipIgnoreCase*(s, token: string, start = 0): int =
+proc skipIgnoreCase*(s, token: openArray[char]): int =
   ## Same as `skip` but case is ignored for token matching.
   runnableExamples:
     doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
     doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
   result = 0
-  while start+result < s.len and result < token.len and
-      toLower(s[result+start]) == toLower(token[result]): inc(result)
+  while result < s.len and result < token.len and
+      toLower(s[result]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
 
-proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: set[char]): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
@@ -301,9 +300,9 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
     doAssert skipUntil("Hello World", {'W'}, 0) == 6
     doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
   result = 0
-  while start+result < s.len and s[result+start] notin until: inc(result)
+  while result < s.len and s[result] notin until: inc(result)
 
-proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: char): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
@@ -313,9 +312,9 @@ proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
     doAssert skipUntil("Hello World", 'W', 0) == 6
     doAssert skipUntil("Hello World", 'w', 0) == 11
   result = 0
-  while start+result < s.len and s[result+start] != until: inc(result)
+  while result < s.len and s[result] != until: inc(result)
 
-proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
+proc skipWhile*(s: openArray[char], toSkip: set[char]): int {.inline.} =
   ## Skips all characters while one char from the set `toSkip` is found.
   ## Returns number of characters skipped.
   runnableExamples:
@@ -323,14 +322,13 @@ proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
     doAssert skipWhile("Hello World", {'e'}) == 0
     doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
   result = 0
-  while start+result < s.len and s[result+start] in toSkip: inc(result)
+  while result < s.len and s[result] in toSkip: inc(result)
 
-proc fastSubstr(s: string; token: var string; start, length: int) =
+proc fastSubstr(s: openArray[char]; token: var string; length: int) =
   token.setLen length
-  for i in 0 ..< length: token[i] = s[i+start]
+  for i in 0 ..< length: token[i] = s[i]
 
-proc parseUntil*(s: string, token: var string, until: set[char],
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: set[char]): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters notin `until`.
@@ -342,14 +340,13 @@ proc parseUntil*(s: string, token: var string, until: set[char],
     doAssert myToken == "Hello "
     doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
     doAssert myToken == "lo "
-  var i = start
+  var i = 0
   while i < s.len and s[i] notin until: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: char,
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: char): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that is not the `until` character.
@@ -361,14 +358,13 @@ proc parseUntil*(s: string, token: var string, until: char,
     doAssert myToken == "Hell"
     doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
     doAssert myToken == "ll"
-  var i = start
+  var i = 0
   while i < s.len and s[i] != until: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: string,
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: string): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that comes before the `until`  token.
@@ -382,7 +378,7 @@ proc parseUntil*(s: string, token: var string, until: string,
     if until.len == 0:
       token.setLen(0)
       return 0
-  var i = start
+  var i = 0
   while i < s.len:
     if until.len > 0 and s[i] == until[0]:
       var u = 1
@@ -390,12 +386,11 @@ proc parseUntil*(s: string, token: var string, until: string,
         inc u
       if u >= until.len: break
     inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseWhile*(s: string, token: var string, validChars: set[char],
-                 start = 0): int {.inline.} =
+proc parseWhile*(s: openArray[char], token: var string, validChars: set[char]): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters in `validChars`.
@@ -405,22 +400,22 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
     doAssert myToken.len() == 0
     doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
     doAssert myToken == "Wor"
-  var i = start
+  var i = 0
   while i < s.len and s[i] in validChars: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
+proc captureBetween*(s: openArray[char], first: char, second = '\0'): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
   ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
   runnableExamples:
     doAssert captureBetween("Hello World", 'e') == "llo World"
     doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
-    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
-  var i = skipUntil(s, first, start)+1+start
+    doAssert captureBetween("Hello World".toOpenArray(6, "Hello World".high), 'l') == "d"
+  var i = skipUntil(s, first) + 1
   result = ""
-  discard s.parseUntil(result, if second == '\0': first else: second, i)
+  discard parseUntil(s.toOpenArray(i, s.high), result, if second == '\0': first else: second)
 
 proc integerOutOfRangeError() {.noinline.} =
   raise newException(ValueError, "Parsed integer outside of valid range")
@@ -429,10 +424,10 @@ proc integerOutOfRangeError() {.noinline.} =
 when defined(js):
   {.push overflowChecks: off.}
 
-proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
+proc rawParseInt(s: openArray[char], b: var BiggestInt): int =
   var
     sign: BiggestInt = -1
-    i = start
+    i = 0
   if i < s.len:
     if s[i] == '+': inc(i)
     elif s[i] == '-':
@@ -452,30 +447,30 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
       integerOutOfRangeError()
     else:
       b = b * sign
-      result = i - start
+      result = i
 
 when defined(js):
   {.pop.} # overflowChecks: off
 
-proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
+proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {.
   rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
     var res: BiggestInt
-    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert parseBiggestInt("9223372036854775807", res) == 19
     doAssert res == 9223372036854775807
   var res = BiggestInt(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseInt(s, res, start)
+  result = rawParseInt(s, res)
   if result != 0:
     number = res
 
-proc parseInt*(s: string, number: var int, start = 0): int {.
+proc parseInt*(s: openArray[char], number: var int): int {.
   rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
@@ -485,14 +480,14 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
     doAssert parseInt("2019", res, 2) == 2
     doAssert res == 19
   var res = BiggestInt(0)
-  result = parseBiggestInt(s, res, start)
+  result = parseBiggestInt(s, res)
   when sizeof(int) <= 4:
     if res < low(int) or res > high(int):
       integerOutOfRangeError()
   if result != 0:
     number = int(res)
 
-proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+proc parseSaturatedNatural*(s: openArray[char], b: var int): int {.
     raises: [].} =
   ## Parses a natural number into ``b``. This cannot raise an overflow
   ## error. ``high(int)`` is returned for an overflow.
@@ -502,7 +497,7 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
     var res = 0
     discard parseSaturatedNatural("848", res)
     doAssert res == 848
-  var i = start
+  var i = 0
   if i < s.len and s[i] == '+': inc(i)
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
@@ -514,13 +509,13 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
         b = high(int)
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    result = i - start
+    result = i
 
-proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
+proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
   var
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
-    i = start
+    i = 0
   if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
     integerOutOfRangeError()
   if i < s.len and s[i] == '+': inc(i) # Allow
@@ -534,11 +529,11 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = res
-    result = i - start
+    result = i
 
-proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
+proc parseBiggestUInt*(s: openArray[char], number: var BiggestUInt): int {.
   rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an unsigned integer starting at `start` and stores the value
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
@@ -550,13 +545,13 @@ proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
   var res = BiggestUInt(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseUInt(s, res, start)
+  result = rawParseUInt(s, res)
   if result != 0:
     number = res
 
-proc parseUInt*(s: string, number: var uint, start = 0): int {.
+proc parseUInt*(s: openArray[char], number: var uint): int {.
   rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an unsigned integer starting at `start` and stores the value
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
@@ -566,22 +561,22 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
     doAssert parseUInt("3450", res, 2) == 2
     doAssert res == 50
   var res = BiggestUInt(0)
-  result = parseBiggestUInt(s, res, start)
+  result = parseBiggestUInt(s, res)
   when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
     if res > 0xFFFF_FFFF'u64:
       integerOutOfRangeError()
   if result != 0:
     number = uint(res)
 
-proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
+proc parseBiggestFloat*(s: openArray[char], number: var BiggestFloat): int {.
   magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
-  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float and stores the value into `number`.
   ## Result is the number of processed chars or 0 if a parsing error
   ## occurred.
 
-proc parseFloat*(s: string, number: var float, start = 0): int {.
+proc parseFloat*(s: openArray[char], number: var float): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
-  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there occurred a parsing
   ## error.
   runnableExamples:
@@ -593,7 +588,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
     doAssert parseFloat("32.57", res, 3) == 2
     doAssert res == 57.00
   var bf = BiggestFloat(0.0)
-  result = parseBiggestFloat(s, bf, start)
+  result = parseBiggestFloat(s, bf)
   if result != 0:
     number = bf
 
@@ -606,7 +601,7 @@ type
     ikVar,                 ## ``var`` part of the interpolated string
     ikExpr                 ## ``expr`` part of the interpolated string
 
-iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKind,
   value: string] =
   ## Tokenizes the string `s` into substrings for interpolation purposes.
   ##
@@ -641,7 +636,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
             else: discard
             inc j
           raise newException(ValueError,
-            "Expected closing '}': " & substr(s, i, s.high))
+            "Expected closing '}': " & substr(s.toOpenArray(i, s.high)))
         inc i, 2 # skip ${
         kind = ikExpr
       elif j+1 < s.len and s[j+1] in IdentStartChars:
@@ -655,15 +650,370 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
         kind = ikDollar
       else:
         raise newException(ValueError,
-          "Unable to parse a variable name at " & substr(s, i, s.high))
+          "Unable to parse a variable name at " & substr(s.toOpenArray(i, s.high)))
     else:
       while j < s.len and s[j] != '$': inc j
       kind = ikStr
     if j > i:
       # do not copy the trailing } for ikExpr:
-      yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
+      yield (kind, substr(s.toOpenArray(i, j-1-ord(kind == ikExpr))))
     else:
       break
     i = j
 
 {.pop.}
+
+
+proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a binary number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-bin character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
+    doAssert num == 5138925
+    doAssert parseBin("3", num) == 0
+    var num8: int8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
+    doAssert num8 == 0b1110_1101'i8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
+    doAssert num8 == 0b0100_1110'i8
+    var num8u: uint8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
+    doAssert num64 == 336784608873
+  parseBin(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses an octal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-oct character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseOct("0o23464755", num) == 10
+    doAssert num == 5138925
+    doAssert parseOct("8", num) == 0
+    var num8: int8
+    doAssert parseOct("0o_1464_755", num8) == 11
+    doAssert num8 == -19
+    doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
+    doAssert num8 == 102
+    var num8u: uint8
+    doAssert parseOct("1464755", num8u) == 7
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseOct("2346475523464755", num64) == 16
+    doAssert num64 == 86216859871725
+  parseOct(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a hexadecimal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-hex character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseHex("4E_69_ED", num) == 8
+    doAssert num == 5138925
+    doAssert parseHex("X", num) == 0
+    doAssert parseHex("#ABC", num) == 4
+    var num8: int8
+    doAssert parseHex("0x_4E_69_ED", num8) == 11
+    doAssert num8 == 0xED'i8
+    doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
+    doAssert num8 == 0x4E'i8
+    var num8u: uint8
+    doAssert parseHex("0x_4E_69_ED", num8u) == 11
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseHex("4E69ED4E69ED", num64) == 12
+    doAssert num64 == 86216859871725
+  parseHex(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseIdent*(s: string, ident: var string, start = 0): int =
+  ## Parses an identifier and stores it in ``ident``. Returns
+  ## the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of `ident` is not changed.
+  runnableExamples:
+    var res: string
+    doAssert parseIdent("Hello World", res, 0) == 5
+    doAssert res == "Hello"
+    doAssert parseIdent("Hello World", res, 1) == 4
+    doAssert res == "ello"
+    doAssert parseIdent("Hello World", res, 6) == 5
+    doAssert res == "World"
+  parseIdent(s.toOpenArray(start, s.high), ident)
+
+proc parseIdent*(s: string, start = 0): string =
+  ## Parses an identifier and returns it or an empty string in
+  ## case of an error.
+  runnableExamples:
+    doAssert parseIdent("Hello World", 0) == "Hello"
+    doAssert parseIdent("Hello World", 1) == "ello"
+    doAssert parseIdent("Hello World", 5) == ""
+    doAssert parseIdent("Hello World", 6) == "World"
+  parseIdent(s.toOpenArray(start, s.high))
+
+proc parseChar*(s: string, c: var char, start = 0): int =
+  ## Parses a single character, stores it in `c` and returns 1.
+  ## In case of error (if start >= s.len) it returns 0
+  ## and the value of `c` is unchanged.
+  runnableExamples:
+    var c: char
+    doAssert "nim".parseChar(c, 3) == 0
+    doAssert c == '\0'
+    doAssert "nim".parseChar(c, 0) == 1
+    doAssert c == 'n'
+  parseChar(s.toOpenArray(start, s.high), c)
+
+proc skipWhitespace*(s: string, start = 0): int {.inline.} =
+  ## Skips the whitespace starting at ``s[start]``. Returns the number of
+  ## skipped characters.
+  runnableExamples:
+    doAssert skipWhitespace("Hello World", 0) == 0
+    doAssert skipWhitespace(" Hello World", 0) == 1
+    doAssert skipWhitespace("Hello World", 5) == 1
+    doAssert skipWhitespace("Hello  World", 5) == 2
+  skipWhitespace(s.toOpenArray(start, s.high))
+
+proc skip*(s, token: string, start = 0): int {.inline.} =
+  ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
+  ## or 0 if there was no `token` at ``s[start]``.
+  runnableExamples:
+    doAssert skip("2019-01-22", "2019", 0) == 4
+    doAssert skip("2019-01-22", "19", 0) == 0
+    doAssert skip("2019-01-22", "19", 2) == 2
+    doAssert skip("CAPlow", "CAP", 0) == 3
+    doAssert skip("CAPlow", "cap", 0) == 0
+  skip(s.toOpenArray(start, s.high), token)
+
+proc skipIgnoreCase*(s, token: string, start = 0): int =
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
+  skipIgnoreCase(s.toOpenArray(start, s.high), token)
+
+proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
+  ## Skips all characters until one char from the set `until` is found
+  ## or the end is reached.
+  ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
+    doAssert skipUntil("Hello World", {'W'}, 0) == 6
+    doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
+  ## Skips all characters until the char `until` is found
+  ## or the end is reached.
+  ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", 'o', 0) == 4
+    doAssert skipUntil("Hello World", 'o', 4) == 0
+    doAssert skipUntil("Hello World", 'W', 0) == 6
+    doAssert skipUntil("Hello World", 'w', 0) == 11
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
+  ## Skips all characters while one char from the set `toSkip` is found.
+  ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipWhile("Hello World", {'H', 'e'}) == 2
+    doAssert skipWhile("Hello World", {'e'}) == 0
+    doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
+  skipWhile(s.toOpenArray(start, s.high), toSkip)
+
+proc parseUntil*(s: string, token: var string, until: set[char],
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of the characters notin `until`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
+    doAssert myToken == "lo "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: char,
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of any character that is not the `until` character.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, 'W') == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, 'o') == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
+    doAssert myToken == "ll"
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: string,
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of any character that comes before the `until`  token.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, "Wor") == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4
+    doAssert myToken == "llo "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseWhile*(s: string, token: var string, validChars: set[char],
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of the characters in `validChars`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0
+    doAssert myToken.len() == 0
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
+    doAssert myToken == "Wor"
+  parseWhile(s.toOpenArray(start, s.high), token, validChars)
+
+proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
+  ## Finds the first occurrence of ``first``, then returns everything from there
+  ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
+  runnableExamples:
+    doAssert captureBetween("Hello World", 'e') == "llo World"
+    doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
+    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
+  captureBetween(s.toOpenArray(start, s.high), first, second)
+
+proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there is no integer.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestInt
+    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert res == 9223372036854775807
+  parseBiggestInt(s.toOpenArray(start, s.high), number)
+
+proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there is no integer.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("2019", res, 0) == 4
+    doAssert res == 2019
+    doAssert parseInt("2019", res, 2) == 2
+    doAssert res == 19
+  parseInt(s.toOpenArray(start, s.high), number)
+
+
+proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+    raises: [].} =
+  ## Parses a natural number into ``b``. This cannot raise an overflow
+  ## error. ``high(int)`` is returned for an overflow.
+  ## The number of processed character is returned.
+  ## This is usually what you really want to use instead of `parseInt`:idx:.
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
+  parseSaturatedNatural(s.toOpenArray(start, s.high), b)
+
+
+proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestUInt
+    doAssert parseBiggestUInt("12", res, 0) == 2
+    doAssert res == 12
+    doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
+    doAssert res == 1111111111111111111'u64
+  parseBiggestUInt(s.toOpenArray(start, s.high), number)
+
+proc parseUInt*(s: string, number: var uint, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: uint
+    doAssert parseUInt("3450", res) == 4
+    doAssert res == 3450
+    doAssert parseUInt("3450", res, 2) == 2
+    doAssert res == 50
+  parseUInt(s.toOpenArray(start, s.high), number)
+
+proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.noSideEffect.} =
+  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if a parsing error
+  ## occurred.
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+proc parseFloat*(s: string, number: var float, start = 0): int {.noSideEffect.} =
+  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there occurred a parsing
+  ## error.
+  runnableExamples:
+    var res: float
+    doAssert parseFloat("32", res, 0) == 2
+    doAssert res == 32.0
+    doAssert parseFloat("32.57", res, 0) == 5
+    doAssert res == 32.57
+    doAssert parseFloat("32.57", res, 3) == 2
+    doAssert res == 57.00
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+  value: string] =
+  ## Tokenizes the string `s` into substrings for interpolation purposes.
+  ##
+  runnableExamples:
+    var outp: seq[tuple[kind: InterpolatedKind, value: string]]
+    for k, v in interpolatedFragments("  $this is ${an  example}  $$"):
+      outp.add (k, v)
+    doAssert outp == @[(ikStr, "  "),
+                       (ikVar, "this"),
+                       (ikStr, " is "),
+                       (ikExpr, "an  example"),
+                       (ikStr, "  "),
+                       (ikDollar, "$")]
+  for x in s.toOa.interpolatedFragments:
+    yield x
+
diff --git a/lib/system.nim b/lib/system.nim
index c0e95a5d2..c28479a44 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2580,7 +2580,19 @@ template once*(body: untyped): untyped =
 
 {.pop.} # warning[GcMem]: off, warning[Uninit]: off
 
-proc substr*(s: string, first, last: int): string =
+proc substr*(s: openArray[char]): string =
+  ## Copies a slice of `s` into a new string and returns this new
+  ## string.
+  runnableExamples:
+    let a = "abcdefgh"
+    assert a.substr(2, 5) == "cdef"
+    assert a.substr(2) == "cdefgh"
+    assert a.substr(5, 99) == "fgh"
+  result = newString(s.len)
+  for i, ch in s:
+    result[i] = ch
+
+proc substr*(s: string, first, last: int): string = # A bug with `magic: Slice` requires this to exist this way
   ## Copies a slice of `s` into a new string and returns this new
   ## string.
   ##
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index a31de0d86..9dfa80877 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -725,15 +725,20 @@ const
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
 
 
-proc parseFloatNative(a: string): float =
-  let a2 = a.cstring
+proc parseFloatNative(a: openarray[char]): float =
+  var str = ""
+  for x in a:
+    str.add x
+
+  let cstr = cstring str
+
   asm """
-  `result` = Number(`a2`);
+  `result` = Number(`cstr`);
   """
 
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int {.compilerproc.} =
+proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} =
   var sign: bool
-  var i = start
+  var i = 0
   if s[i] == '+': inc(i)
   elif s[i] == '-':
     sign = true
@@ -743,14 +748,14 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int
       if s[i+2] == 'N' or s[i+2] == 'n':
         if s[i+3] notin IdentChars:
           number = NaN
-          return i+3 - start
+          return i+3
     return 0
   if s[i] == 'I' or s[i] == 'i':
     if s[i+1] == 'N' or s[i+1] == 'n':
       if s[i+2] == 'F' or s[i+2] == 'f':
         if s[i+3] notin IdentChars:
           number = if sign: -Inf else: Inf
-          return i+3 - start
+          return i+3
     return 0
 
   var buf: string
@@ -782,7 +787,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int
       addInc()
       eatUnderscores()
   number = parseFloatNative(buf)
-  result = i - start
+  result = i
 
 # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce
 # 'Math.trunc' for Nim's ``div`` and ``mod`` operators:
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index feaac7817..cb67185ab 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -78,8 +78,8 @@ const
 when defined(nimHasInvariant):
   {.push staticBoundChecks: off.}
 
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
-                          start = 0): int {.compilerproc.} =
+proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat,
+                         ): int {.compilerproc.} =
   # This routine attempt to parse float that can parsed quickly.
   # i.e. whose integer part can fit inside a 53bits integer.
   # their real exponent must also be <= 22. If the float doesn't follow
@@ -88,7 +88,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   # This avoid the problems of decimal character portability.
   # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
   var
-    i = start
+    i = 0
     sign = 1.0
     kdigits, fdigits = 0
     exponent = 0
@@ -111,7 +111,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       if s[i+2] == 'N' or s[i+2] == 'n':
         if i+3 >= s.len or s[i+3] notin IdentChars:
           number = NaN
-          return i+3 - start
+          return i+3
     return 0
 
   # Inf?
@@ -120,7 +120,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       if s[i+2] == 'F' or s[i+2] == 'f':
         if i+3 >= s.len or s[i+3] notin IdentChars:
           number = Inf*sign
-          return i+3 - start
+          return i+3
     return 0
 
   if i < s.len and s[i] in {'0'..'9'}:
@@ -154,8 +154,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # if has no digits: return error
   if kdigits + fdigits <= 0 and
-     (i == start or # no char consumed (empty string).
-     (i == start + 1 and hasSign)): # or only '+' or '-
+     (i == 0 or # no char consumed (empty string).
+     (i == 1 and hasSign)): # or only '+' or '-
     return 0
 
   if i+1 < s.len and s[i] in {'e', 'E'}:
@@ -182,7 +182,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       number = 0.0*sign
     else:
       number = Inf*sign
-    return i - start
+    return i
 
   # if integer is representable in 53 bits:  fast path
   # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
@@ -194,14 +194,14 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
         number = sign * integer.float / powtens[absExponent]
       else:
         number = sign * integer.float * powtens[absExponent]
-      return i - start
+      return i
 
     # if exponent is greater try to fit extra exponent above 22 by multiplying
     # integer part is there is space left.
     let slop = 15 - kdigits - fdigits
     if absExponent <= 22 + slop and not expNegative:
       number = sign * integer.float * powtens[slop] * powtens[absExponent-slop]
-      return i - start
+      return i
 
   # if failed: slow path with strtod.
   var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
@@ -209,8 +209,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   let maxlen = t.high - "e+000".len # reserve enough space for exponent
 
   let endPos = i
-  result = endPos - start
-  i = start
+  result = endPos
+  i = 0
   # re-parse without error checking, any error should be handled by the code above.
   if i < endPos and s[i] == '.': i.inc
   while i < endPos and s[i] in {'0'..'9','+','-'}: