Improve exception usage in parseutils (#10284)
This commit is contained in:
parent
ece5887ab0
commit
1d7e3a84fd
2 changed files with 34 additions and 24 deletions
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
|
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
|
||||||
|
|
||||||
|
- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`, `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a `ValueError` when the parsed integer is outside of the valid range. Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
|
||||||
|
|
||||||
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
|
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
|
||||||
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
|
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
|
||||||
check if a group is captured, otherwise you'll recieve an exception.
|
check if a group is captured, otherwise you'll recieve an exception.
|
||||||
|
|
|
||||||
|
|
@ -242,8 +242,9 @@ proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
|
||||||
result = ""
|
result = ""
|
||||||
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
discard s.parseUntil(result, if second == '\0': first else: second, i)
|
||||||
|
|
||||||
{.push overflowChecks: on.}
|
template integerOutOfRangeError(): ref ValueError =
|
||||||
# this must be compiled with overflow checking turned on:
|
newException(ValueError, "Parsed integer outside of valid range")
|
||||||
|
|
||||||
proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
|
proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
|
||||||
var
|
var
|
||||||
sign: BiggestInt = -1
|
sign: BiggestInt = -1
|
||||||
|
|
@ -256,38 +257,45 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
|
||||||
if i < s.len and s[i] in {'0'..'9'}:
|
if i < s.len and s[i] in {'0'..'9'}:
|
||||||
b = 0
|
b = 0
|
||||||
while i < s.len and s[i] in {'0'..'9'}:
|
while i < s.len and s[i] in {'0'..'9'}:
|
||||||
b = b * 10 - (ord(s[i]) - ord('0'))
|
let c = ord(s[i]) - ord('0')
|
||||||
|
if b >= (low(int) + c) div 10:
|
||||||
|
b = b * 10 - c
|
||||||
|
else:
|
||||||
|
raise integerOutOfRangeError()
|
||||||
inc(i)
|
inc(i)
|
||||||
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||||
|
if sign == -1 and b == low(int):
|
||||||
|
raise integerOutOfRangeError()
|
||||||
b = b * sign
|
b = b * sign
|
||||||
result = i - start
|
result = i - start
|
||||||
{.pop.} # overflowChecks
|
|
||||||
|
|
||||||
proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
|
proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
|
||||||
rtl, extern: "npuParseBiggestInt", noSideEffect.} =
|
rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
|
||||||
## parses an integer starting at `start` and stores the value into `number`.
|
## 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.
|
## Result is the number of processed chars or 0 if there is no integer.
|
||||||
## `OverflowError` is raised if an overflow occurs.
|
## `ValueError` is raised if the parsed integer is out of the valid range.
|
||||||
var res: BiggestInt
|
var res: BiggestInt
|
||||||
# use 'res' for exception safety (don't write to 'number' in case of an
|
# use 'res' for exception safety (don't write to 'number' in case of an
|
||||||
# overflow exception):
|
# overflow exception):
|
||||||
result = rawParseInt(s, res, start)
|
result = rawParseInt(s, res, start)
|
||||||
number = res
|
if result != 0:
|
||||||
|
number = res
|
||||||
|
|
||||||
proc parseInt*(s: string, number: var int, start = 0): int {.
|
proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||||
rtl, extern: "npuParseInt", noSideEffect.} =
|
rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
|
||||||
## parses an integer starting at `start` and stores the value into `number`.
|
## 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.
|
## Result is the number of processed chars or 0 if there is no integer.
|
||||||
## `OverflowError` is raised if an overflow occurs.
|
## `ValueError` is raised if the parsed integer is out of the valid range.
|
||||||
var res: BiggestInt
|
var res: BiggestInt
|
||||||
result = parseBiggestInt(s, res, start)
|
result = parseBiggestInt(s, res, start)
|
||||||
if (sizeof(int) <= 4) and
|
when sizeof(int) <= 4:
|
||||||
((res < low(int)) or (res > high(int))):
|
if res < low(int) or res > high(int):
|
||||||
raise newException(OverflowError, "overflow")
|
raise integerOutOfRangeError()
|
||||||
elif result != 0:
|
if result != 0:
|
||||||
number = int(res)
|
number = int(res)
|
||||||
|
|
||||||
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
|
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
|
||||||
|
raises: [].}=
|
||||||
## parses a natural number into ``b``. This cannot raise an overflow
|
## parses a natural number into ``b``. This cannot raise an overflow
|
||||||
## error. ``high(int)`` is returned for an overflow.
|
## error. ``high(int)`` is returned for an overflow.
|
||||||
## The number of processed character is returned.
|
## The number of processed character is returned.
|
||||||
|
|
@ -312,12 +320,13 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
|
||||||
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||||
result = i - start
|
result = i - start
|
||||||
|
|
||||||
# overflowChecks doesn't work with BiggestUInt
|
|
||||||
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
|
proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
|
||||||
var
|
var
|
||||||
res = 0.BiggestUInt
|
res = 0.BiggestUInt
|
||||||
prev = 0.BiggestUInt
|
prev = 0.BiggestUInt
|
||||||
i = start
|
i = start
|
||||||
|
if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
|
||||||
|
raise integerOutOfRangeError()
|
||||||
if i < s.len and s[i] == '+': inc(i) # Allow
|
if i < s.len and s[i] == '+': inc(i) # Allow
|
||||||
if i < s.len and s[i] in {'0'..'9'}:
|
if i < s.len and s[i] in {'0'..'9'}:
|
||||||
b = 0
|
b = 0
|
||||||
|
|
@ -325,35 +334,34 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
|
||||||
prev = res
|
prev = res
|
||||||
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
|
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
|
||||||
if prev > res:
|
if prev > res:
|
||||||
return 0 # overflowChecks emulation
|
raise integerOutOfRangeError()
|
||||||
inc(i)
|
inc(i)
|
||||||
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
|
||||||
b = res
|
b = res
|
||||||
result = i - start
|
result = i - start
|
||||||
|
|
||||||
proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
|
proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
|
||||||
rtl, extern: "npuParseBiggestUInt", noSideEffect.} =
|
rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
|
||||||
## parses an unsigned integer starting at `start` and stores the value
|
## parses an unsigned integer starting at `start` and stores the value
|
||||||
## into `number`.
|
## 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.
|
||||||
## or overflow detected.
|
|
||||||
var res: BiggestUInt
|
var res: BiggestUInt
|
||||||
# use 'res' for exception safety (don't write to 'number' in case of an
|
# use 'res' for exception safety (don't write to 'number' in case of an
|
||||||
# overflow exception):
|
# overflow exception):
|
||||||
result = rawParseUInt(s, res, start)
|
result = rawParseUInt(s, res, start)
|
||||||
number = res
|
if result != 0:
|
||||||
|
number = res
|
||||||
|
|
||||||
proc parseUInt*(s: string, number: var uint, start = 0): int {.
|
proc parseUInt*(s: string, number: var uint, start = 0): int {.
|
||||||
rtl, extern: "npuParseUInt", noSideEffect.} =
|
rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
|
||||||
## parses an unsigned integer starting at `start` and stores the value
|
## parses an unsigned integer starting at `start` and stores the value
|
||||||
## into `number`.
|
## into `number`.
|
||||||
## Result is the number of processed chars or 0 if there is no integer or
|
## `ValueError` is raised if the parsed integer is out of the valid range.
|
||||||
## overflow detected.
|
|
||||||
var res: BiggestUInt
|
var res: BiggestUInt
|
||||||
result = parseBiggestUInt(s, res, start)
|
result = parseBiggestUInt(s, res, start)
|
||||||
when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
|
when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
|
||||||
if res > 0xFFFF_FFFF'u64:
|
if res > 0xFFFF_FFFF'u64:
|
||||||
raise newException(OverflowError, "overflow")
|
raise integerOutOfRangeError()
|
||||||
if result != 0:
|
if result != 0:
|
||||||
number = uint(res)
|
number = uint(res)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue