Rework nil/option for nre in light of ""==nil

Fixes #9243
This commit is contained in:
Flaviu Tamas 2018-11-18 21:41:40 -05:00
commit c0a47f7e22
No known key found for this signature in database
GPG key ID: D6BF32C876496756
6 changed files with 126 additions and 87 deletions

View file

@ -65,7 +65,7 @@ runnableExamples:
let hasVowel = firstVowel.isSome()
if hasVowel:
let matchBounds = firstVowel.get().captureBounds[-1]
doAssert matchBounds.get().a == 1
doAssert matchBounds.a == 1
# Type definitions {{{
@ -167,14 +167,15 @@ type
## - ``"abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"``
## - ``"abc".match(re"(\w)\w").get.captures[-1] == "ab"``
##
## ``captureBounds[]: Option[HSlice[int, int]]``
## ``captureBounds[]: HSlice[int, int]``
## gets the bounds of the given capture according to the same rules as
## the above. If the capture is not filled, then ``None`` is returned.
## The bounds are both inclusive.
##
## - ``"abc".match(re"(\w)").get.captureBounds[0].get == 0 .. 0``
## - ``"abc".match(re"").get.captureBounds[-1].get == 0 .. -1``
## - ``"abc".match(re"abc").get.captureBounds[-1].get == 0 .. 2``
## - ``"abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0``
## - ``0 in "abc".match(re"(\w)").get.captureBounds == true``
## - ``"abc".match(re"").get.captureBounds[-1] == 0 .. -1``
## - ``"abc".match(re"abc").get.captureBounds[-1] == 0 .. 2``
##
## ``match: string``
## the full text of the match.
@ -227,9 +228,10 @@ runnableExamples:
doAssert "abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"
doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
doAssert "abc".match(re"(\w)").get.captureBounds[0].get == 0 .. 0
doAssert "abc".match(re"").get.captureBounds[-1].get == 0 .. -1
doAssert "abc".match(re"abc").get.captureBounds[-1].get == 0 .. 2
doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
doAssert 0 in "abc".match(re"(\w)").get.captureBounds == true
doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
# }}}
proc getinfo[T](pattern: Regex, opt: cint): T =
@ -269,78 +271,99 @@ proc matchesCrLf(pattern: Regex): bool =
# }}}
# Capture accessors {{{
proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
func captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
func captures*(pattern: RegexMatch): Captures = return Captures(pattern)
proc `[]`*(pattern: CaptureBounds, i: int): Option[HSlice[int, int]] =
func contains*(pattern: CaptureBounds, i: int): bool =
let pattern = RegexMatch(pattern)
if pattern.pcreMatchBounds[i + 1].a != -1:
let bounds = pattern.pcreMatchBounds[i + 1]
return some(int(bounds.a) .. int(bounds.b-1))
else:
return none(HSlice[int, int])
pattern.pcreMatchBounds[i + 1].a != -1
proc `[]`*(pattern: Captures, i: int): string =
func contains*(pattern: Captures, i: int): bool =
i in CaptureBounds(pattern)
func `[]`*(pattern: CaptureBounds, i: int): HSlice[int, int] =
let pattern = RegexMatch(pattern)
if not (i in pattern.captureBounds):
raise newException(IndexError, "Group '" & $i & "' was not captured")
let bounds = pattern.pcreMatchBounds[i + 1]
int(bounds.a)..int(bounds.b-1)
func `[]`*(pattern: Captures, i: int): string =
let pattern = RegexMatch(pattern)
let bounds = pattern.captureBounds[i]
if bounds.isSome:
let bounds = bounds.get
return pattern.str.substr(bounds.a, bounds.b)
else:
return nil
pattern.str.substr(bounds.a, bounds.b)
proc match*(pattern: RegexMatch): string =
func match*(pattern: RegexMatch): string =
return pattern.captures[-1]
proc matchBounds*(pattern: RegexMatch): HSlice[int, int] =
return pattern.captureBounds[-1].get
func matchBounds*(pattern: RegexMatch): HSlice[int, int] =
return pattern.captureBounds[-1]
proc `[]`*(pattern: CaptureBounds, name: string): Option[HSlice[int, int]] =
func contains*(pattern: CaptureBounds, name: string): bool =
let pattern = RegexMatch(pattern)
return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
let nameToId = pattern.pattern.captureNameToId
if not (name in nameToId):
return false
nameToId[name] in pattern.captureBounds
proc `[]`*(pattern: Captures, name: string): string =
func contains*(pattern: Captures, name: string): bool =
name in CaptureBounds(pattern)
func checkNamedCaptured(pattern: RegexMatch, name: string): void =
if not (name in pattern.captureBounds):
raise newException(KeyError, "Group '" & name & "' was not captured")
func `[]`*(pattern: CaptureBounds, name: string): HSlice[int, int] =
let pattern = RegexMatch(pattern)
return pattern.captures[pattern.pattern.captureNameToId.fget(name)]
checkNamedCaptured(pattern, name)
pattern.captureBounds[pattern.pattern.captureNameToId[name]]
template toTableImpl(cond: untyped) {.dirty.} =
func `[]`*(pattern: Captures, name: string): string =
let pattern = RegexMatch(pattern)
checkNamedCaptured(pattern, name)
return pattern.captures[pattern.pattern.captureNameToId[name]]
template toTableImpl() {.dirty.} =
for key in RegexMatch(pattern).pattern.captureNameId.keys:
let nextVal = pattern[key]
if cond:
result[key] = default
else:
result[key] = nextVal
if key in pattern:
result[key] = pattern[key]
proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
func toTable*(pattern: Captures): Table[string, string] =
result = initTable[string, string]()
toTableImpl(nextVal == nil)
toTableImpl()
proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
Table[string, Option[HSlice[int, int]]] =
result = initTable[string, Option[HSlice[int, int]]]()
toTableImpl(nextVal.isNone)
func toTable*(pattern: CaptureBounds): Table[string, HSlice[int, int]] =
result = initTable[string, HSlice[int, int]]()
toTableImpl()
template itemsImpl(cond: untyped) {.dirty.} =
template itemsImpl() {.dirty.} =
for i in 0 ..< RegexMatch(pattern).pattern.captureCount:
let nextVal = pattern[i]
# done in this roundabout way to avoid multiple yields (potential code
# bloat)
let nextYieldVal = if cond: default else: nextVal
let nextYieldVal = if i in pattern:
some(pattern[i])
else:
default
yield nextYieldVal
iterator items*(pattern: CaptureBounds,
default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl()
iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl(nextVal.isNone)
iterator items*(pattern: Captures,
default: Option[string] = none(string)): Option[string] =
itemsImpl()
iterator items*(pattern: Captures, default: string = nil): string =
itemsImpl(nextVal == nil)
proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
proc toSeq*(pattern: CaptureBounds,
default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
accumulateResult(pattern.items(default))
proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
proc toSeq*(pattern: Captures,
default: Option[string] = none(string)): seq[Option[string]] =
accumulateResult(pattern.items(default))
proc `$`*(pattern: RegexMatch): string =
@ -652,7 +675,8 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
for cap in match.captures:
# if there are captures, include them in the result
result.add(cap)
if cap.isSome:
result.add(cap.get)
if splits == maxSplit - 1:
break
@ -706,7 +730,8 @@ proc replace*(str: string, pattern: Regex,
## - ``$#`` - first capture
## - ``$0`` - full match
##
## If a given capture is missing, a ``ValueError`` exception is thrown.
## If a given capture is missing, ``IndexError`` thrown for un-named captures
## and ``KeyError`` for named captures.
replaceImpl(str, pattern, subproc(match))
proc replace*(str: string, pattern: Regex,