diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 31598f8c6f..3545d1fcdc 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -56,10 +56,12 @@ const IdentStartChars = {'a'..'z', 'A'..'Z', '_'} ## copied from strutils +template toOa(s: string, start: int): openArray[char] = s.toOpenArray(start, s.high) + 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, +proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = ## Parses a binary number and stores its value in ``number``. ## @@ -67,8 +69,8 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, ## 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. + ## or to the end of the array. Otherwise, no more than ``maxLen`` characters + ## are from the array. ## ## 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 @@ -89,7 +91,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,9 +106,19 @@ 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, +proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = + ## `string` overload of `parseBin`_ + ## 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. + ## see `parseBin` for further docs + s.toOa(start).parseBin(number, maxLen) + + +proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = ## Parses an octal number and stores its value in ``number``. ## @@ -115,7 +127,7 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, ## ## 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. + ## are from the array. ## ## 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 @@ -136,7 +148,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,9 +163,18 @@ 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, +proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = + ## `string` overload of `parseOct`_ + ## 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. + s.toOa(start).parseOct(number, maxLen) + + +proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = ## Parses a hexadecimal number and stores its value in ``number``. ## @@ -184,7 +205,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 +227,17 @@ 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 parseHex*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = + ## `string` overload of `parseHex`_ + ## 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. + s.toOa(start).parseHex(number, maxLen) + +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 +249,19 @@ 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, 0, i-1) + result = i -proc parseIdent*(s: string, start = 0): string = +proc parseIdent*(s: string, ident: var string, start = 0): int = + ## `string` overload of `parseIdent`_ + s.toOa(start).parseIdent(ident) + + +proc parseIdent*(s: openArray[char]): string = ## Parses an identifier and returns it or an empty string in ## case of an error. runnableExamples: @@ -236,13 +270,17 @@ 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, 0, i-1) -proc parseChar*(s: string, c: var char, start = 0): int = +proc parseIdent*(s: string, start = 0): string = + ## `string` overload of `parseIdent`_ + s.toOa(start).parseIdent() + +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,12 +290,17 @@ 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 0 < s.len: + c = s[0] result = 1 -proc skipWhitespace*(s: string, start = 0): int {.inline.} = - ## Skips the whitespace starting at ``s[start]``. Returns the number of +proc parseChar*(s: string, c: var char, start = 0): int = + ## `string` overload of `parseChar`_ + s.toOa(start).parseChar(c) + + +proc skipWhitespace*(s: openArray[char]): int {.inline.} = + ## Skips whitespace at the start of ``s``. Returns the number of ## skipped characters. runnableExamples: doAssert skipWhitespace("Hello World", 0) == 0 @@ -265,11 +308,15 @@ 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.} = - ## Skips the `token` starting at ``s[start]``. Returns the length of `token` - ## or 0 if there was no `token` at ``s[start]``. +proc skipWhitespace*(s: string, start = 0): int {.inline.} = + ## `string` overload of `skipWhitespace`_ + s.toOa(start).skipWhitespace() + +proc skip*(s, token: openArray[char]): int {.inline.} = + ## Skips the `token` starting ``s`. Returns the length of `token` + ## or 0 if there was no `token` at the start of ``s``. runnableExamples: doAssert skip("2019-01-22", "2019", 0) == 4 doAssert skip("2019-01-22", "19", 0) == 0 @@ -277,22 +324,30 @@ 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 skip*(s: string, token: openArray[char], start = 0): int {.inline.} = + ## `string` overload of `skip`_ + s.toOa(start).skip(token) + +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 skipIgnoreCase*(s: string, token: openArray[char], start = 0): int = + ## `string` overload of `skipIgnoreCase`_ + s.toOa(start).skipIgnoreCase(token) + +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 +356,14 @@ 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: string, until: set[char], start = 0): int {.inline.} = + ## `string` overload of `skipUntil`_ + s.toOa(start).skipUntil(until) + + +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 +373,14 @@ 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 skipUntil*(s: string, until: char, start = 0): int {.inline.} = + ## `string` overload of `skipUntil`_ + s.toOa(start).skipUntil(until) + + +proc skipWhile*(s: openArray[char], toSkip: set[char]): int {.inline.} = ## Skips all characters while one char from the set `token` is found. ## Returns number of characters skipped. runnableExamples: @@ -323,14 +388,21 @@ 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 skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = + ## `string` overload of `skipWhile`_ + s.toOa(start).skipWhile(toSkip) + +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 fastSubstr(s: string; token: var string; start, length: int) = + s.toOa(start).fastSubstr(token, length) + + +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 +414,16 @@ 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 - while i < s.len and s[i] notin until: inc(i) - result = i-start - fastSubstr(s, token, start, result) + while result < s.len and s[result] notin until: inc(result) + fastSubstr(s, token, result) #token = substr(s, start, i-1) -proc parseUntil*(s: string, token: var string, until: char, +proc parseUntil*(s: string, token: var string, until: set[char], start = 0): int {.inline.} = + ## `string` overload of `parseUntil`_ + s.toOa(start).parseUntil(token, until) + +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 +435,16 @@ 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 - while i < s.len and s[i] != until: inc(i) - result = i-start - fastSubstr(s, token, start, result) + while result < s.len and s[result] != until: inc result + fastSubstr(s, token, result) #token = substr(s, start, i-1) -proc parseUntil*(s: string, token: var string, until: string, +proc parseUntil*(s: string, token: var string, until: char, start = 0): int {.inline.} = + ## `string` overload of `parseUntil`_ + s.toOa(start).parseUntil(token, until) + +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. @@ -378,20 +454,23 @@ proc parseUntil*(s: string, token: var string, until: string, doAssert myToken == "Hello " doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4 doAssert myToken == "llo " - var i = start - while i < s.len: - if until.len > 0 and s[i] == until[0]: + + while result < s.len: + if until.len > 0 and s[result] == until[0]: var u = 1 - while i+u < s.len and u < until.len and s[i+u] == until[u]: + while result+u < s.len and u < until.len and s[result+u] == until[u]: inc u if u >= until.len: break - inc(i) - result = i-start - fastSubstr(s, token, start, result) + inc result + fastSubstr(s, token, result) #token = substr(s, start, i-1) -proc parseWhile*(s: string, token: var string, validChars: set[char], +proc parseUntil*(s: string, token: var string, until: string, start = 0): int {.inline.} = + ## `string` overload of `parseUntil`_ + s.toOa(start).parseUntil(token, until) + +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`. @@ -401,22 +480,30 @@ 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 - while i < s.len and s[i] in validChars: inc(i) - result = i-start - fastSubstr(s, token, start, result) + while result < s.len and s[result] in validChars: inc result + fastSubstr(s, token, result) #token = substr(s, start, i-1) -proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = +proc parseWhile*(s: string, token: var string, validChars: set[char], + start = 0): int {.inline.} = + ## `string` overload of `parseWhile`_ + s.toOa(start).parseWhile(token, validChars) + + +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 + var i = skipUntil(s, first)+1 result = "" - discard s.parseUntil(result, if second == '\0': first else: second, i) + discard s.toOpenArray(i, s.high).parseUntil(result, if second == '\0': first else: second) + +proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = + ## `string` overload of `captureBetween`_ + s.toOa(start).captureBetween(first, second) proc integerOutOfRangeError() {.noinline.} = raise newException(ValueError, "Parsed integer outside of valid range") @@ -425,10 +512,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] == '-': @@ -448,12 +535,12 @@ 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`. ## Result is the number of processed chars or 0 if there is no integer. @@ -465,11 +552,14 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {. 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 parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSideEffect, raises: [ValueError].} = + s.toOa(start).parseBiggestInt(number) + +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`. ## Result is the number of processed chars or 0 if there is no integer. @@ -481,14 +571,18 @@ 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 parseInt*(s: string, number: var int, start = 0): int = + ## `string` overload of `parseInt`_ + s.toOa(start).parseInt(number) + +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. @@ -498,7 +592,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 @@ -510,13 +604,18 @@ 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 parseSaturatedNatural*(s: string, b: var int, start = 0): int {. + raises: [].} = + ## `string` overload + s.toOa(start).parseSaturatedNatural(b) + +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 @@ -530,9 +629,9 @@ 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 ## into `number`. @@ -546,11 +645,15 @@ 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 parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int = + ## `string` overload + s.toOa(start).parseBiggestUInt(number) + +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 ## into `number`. @@ -562,20 +665,24 @@ 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 parseUInt*(s: string, number: var uint, start = 0): int = + ## `string` overload + s.toOa(start).parseUInt(number) + +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`. ## 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, start = 0): int {. rtl, extern: "npuParseFloat", 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 @@ -589,10 +696,14 @@ 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 +proc parseFloat*(s: string, number: var float, start = 0): int = + ## `string` overload + s.toOa(start).parseFloat(number) + type InterpolatedKind* = enum ## Describes for `interpolatedFragments` ## which part of the interpolated string is @@ -602,9 +713,9 @@ 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. + ## Tokenizes the array `s` into substrings for interpolation purposes. ## runnableExamples: var outp: seq[tuple[kind: InterpolatedKind, value: string]] diff --git a/lib/system.nim b/lib/system.nim index 1effd48c5f..5512fc6786 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2986,7 +2986,7 @@ 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], first, last: int): string = ## Copies a slice of `s` into a new string and returns this new ## string. ## @@ -3007,7 +3007,7 @@ proc substr*(s: string, first, last: int): string = for i in 0 .. L-1: result[i] = s[i+first] -proc substr*(s: string, first = 0): string = +proc substr*(s: openArray[char], first = 0): string = result = substr(s, first, high(s)) when defined(nimscript): diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 6644a0d16e..3c5c46a09f 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -55,8 +55,7 @@ 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 @@ -65,7 +64,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 @@ -88,7 +87,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? @@ -97,7 +96,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'}: @@ -131,8 +130,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 == 0 + 1 and hasSign)): # or only '+' or '- return 0 if i+1 < s.len and s[i] in {'e', 'E'}: @@ -159,7 +158,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) @@ -171,14 +170,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 @@ -186,8 +185,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','+','-'}: