parent
22b3e9df27
commit
c0a47f7e22
6 changed files with 126 additions and 87 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,9 @@
|
|||
## INTERNAL FILE FOR USE ONLY BY nre.nim.
|
||||
import tables
|
||||
|
||||
proc fget*[K, V](self: Table[K, V], key: K): V =
|
||||
if self.hasKey(key):
|
||||
return self[key]
|
||||
else:
|
||||
raise newException(KeyError, "Key does not exist in table: " & $key)
|
||||
|
||||
const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||
const StartIdent = Ident - {'0'..'9'}
|
||||
|
||||
proc checkNil(arg: string): string =
|
||||
if arg == nil:
|
||||
raise newException(ValueError, "Cannot use nil capture")
|
||||
else:
|
||||
return arg
|
||||
|
||||
template formatStr*(howExpr, namegetter, idgetter): untyped =
|
||||
let how = howExpr
|
||||
var val = newStringOfCap(how.len)
|
||||
|
|
@ -32,7 +20,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
|
|||
i += 2
|
||||
elif how[i + 1] == '#':
|
||||
var id {.inject.} = lastNum
|
||||
val.add(checkNil(idgetter))
|
||||
val.add(idgetter)
|
||||
lastNum += 1
|
||||
i += 2
|
||||
elif how[i + 1] in {'0'..'9'}:
|
||||
|
|
@ -41,7 +29,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
|
|||
while i < how.len and how[i] in {'0'..'9'}:
|
||||
id += (id * 10) + (ord(how[i]) - ord('0'))
|
||||
i += 1
|
||||
val.add(checkNil(idgetter))
|
||||
val.add(idgetter)
|
||||
lastNum = id + 1
|
||||
elif how[i + 1] in StartIdent:
|
||||
i += 1
|
||||
|
|
@ -49,7 +37,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
|
|||
while i < how.len and how[i] in Ident:
|
||||
name.add(how[i])
|
||||
i += 1
|
||||
val.add(checkNil(namegetter))
|
||||
val.add(namegetter)
|
||||
elif how[i + 1] == '{':
|
||||
i += 2
|
||||
var name {.inject.} = ""
|
||||
|
|
@ -57,7 +45,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
|
|||
name.add(how[i])
|
||||
i += 1
|
||||
i += 1
|
||||
val.add(checkNil(namegetter))
|
||||
val.add(namegetter)
|
||||
else:
|
||||
raise newException(Exception, "Syntax error in format string at " & $i)
|
||||
val
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue