diff --git a/CHANGELOG.md b/CHANGELOG.md index ec3ece5..7865efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +v0.13 +================== + +* Add `groupFirstCapture`, `groupLastCapture`, and + `group(1): seq[string]` (thaks to @xmonader) +* Add Nim 1.0.0 to CI +* Drop Nim 0.18 support +* Fix nested captures with repetition range; issue #46 +* Fix Nim `sets` warnings + v0.12 ================== diff --git a/docs/index.html b/docs/index.html index 507a59a..6dcc934 100644 --- a/docs/index.html +++ b/docs/index.html @@ -99,6 +99,23 @@ width: 80%; } +/* + * Some custom formatting for input forms. + * This also fixes input form colors on Firefox with a dark system theme on Linux. + */ +input { + -moz-appearance: none; + color: #333; + background-color: #f8f8f8; + border: 1px solid #aaa; + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-size: 0.9em; + padding: 6px; +} +input:focus { + border: 1px solid #1fa0eb; + box-shadow: 0 0 2px #1fa0eb; +} /* Docgen styles */ /* Links */ @@ -825,12 +842,12 @@

regex

title="Regex = object states: seq[Node] groupsCount: int16 - namedGroups: Table[string, int16]">Regex + namedGroups: OrderedTable[string, int16]">Regex
  • RegexMatch
  • @@ -844,6 +861,14 @@

    regex

    title="group(m: RegexMatch; s: string): seq[Slice[int]]">groupRegexMatch
  • groupsCountRegexMatch
  • +
  • groupNamesRegexMatch
  • +
  • groupRegexMatch
  • +
  • groupFirstCaptureRegexMatch
  • +
  • groupLastCaptureRegexMatch
  • matchRegex
  • Types
    Regex = object
       states: seq[Node]
       groupsCount: int16
    -  namedGroups: Table[string, int16]
    +  namedGroups: OrderedTable[string, int16]
     
    @@ -1003,7 +1028,7 @@

    Types

    RegexMatch = object
       captures: seq[Slice[int]]
       groups: seq[Slice[int]]
    -  namedGroups: Table[string, int16]
    +  namedGroups: OrderedTable[string, int16]
       boundaries*: Slice[int]
     
    @@ -1034,14 +1059,65 @@

    Procs

    proc groupsCount(m: RegexMatch): int {...}{.raises: [], tags: [].}
    -return the number of capturing groups
    block:
    -  var m: RegexMatch
    -  doAssert "ab".match(re"(a)(b)", m)
    -  doAssert m.groupsCount == 2
    -block:
    -  var m: RegexMatch
    -  doAssert "ab".match(re"((ab))", m)
    -  doAssert m.groupsCount == 2
    +return the number of capturing groups +

    Examples:

    +
    var m: RegexMatch
    +doAssert "ab".match(re"(a)(b)", m)
    +doAssert m.groupsCount == 2
    + +
    + +
    proc groupNames(m: RegexMatch): seq[string] {...}{.raises: [], tags: [].}
    +
    + +return the names of capturing groups. +

    Examples:

    +
    let text = "hello world"
    +var m: RegexMatch
    +doAssert text.match(re"(?P<greet>hello) (?P<who>world)", m)
    +doAssert m.groupNames() == @["greet", "who"]
    + +
    + +
    proc group(m: RegexMatch; groupName: string; text: string): seq[string] {...}{.
    +    raises: [KeyError], tags: [].}
    +
    + +return seq of captured text by group groupName +

    Examples:

    +
    let text = "hello beautiful world"
    +var m: RegexMatch
    +doAssert text.match(re"(?P<greet>hello) (?:(?P<who>[^\s]+)\s?)+", m)
    +doAssert m.group("greet", text) == @["hello"]
    +doAssert m.group("who", text) == @["beautiful", "world"]
    + +
    + +
    proc groupFirstCapture(m: RegexMatch; groupName: string; text: string): string {...}{.
    +    raises: [KeyError], tags: [].}
    +
    + +Return fist capture for a given capturing group +

    Examples:

    +
    let text = "hello beautiful world"
    +var m: RegexMatch
    +doAssert text.match(re"(?P<greet>hello) (?:(?P<who>[^\s]+)\s?)+", m)
    +doAssert m.groupFirstCapture("greet", text) == "hello"
    +doAssert m.groupFirstCapture("who", text) == "beautiful"
    + +
    + +
    proc groupLastCapture(m: RegexMatch; groupName: string; text: string): string {...}{.
    +    raises: [KeyError], tags: [].}
    +
    + +Return last capture for a given capturing group +

    Examples:

    +
    let text = "hello beautiful world"
    +var m: RegexMatch
    +doAssert text.match(re"(?P<greet>hello) (?:(?P<who>[^\s]+)\s?)+", m)
    +doAssert m.groupLastCapture("greet", text) == "hello"
    +doAssert m.groupLastCapture("who", text) == "world"
    @@ -1049,18 +1125,22 @@

    Procs

    tags: [].}
    -return a match if the whole string matches the regular expression. This is similar to find(text, re"^regex$") but has better performance
    var m: RegexMatch
    -doAssert "abcd".match(re"abcd", m)
    -doAssert(not "abcd".match(re"abc", m))
    +return a match if the whole string matches the regular expression. This is similar to find(text, re"^regex$") but has better performance +

    Examples:

    +
    var m: RegexMatch
    +doAssert "abcd".match(re"abcd", m)
    +doAssert(not "abcd".match(re"abc", m))
    proc contains(s: string; pattern: Regex): bool {...}{.raises: [], tags: [].}
    -search for the pattern anywhere in the string. It returns as soon as there is a match, even when the expression has repetitions. Use re"^regex$" to match the whole string instead of searching
    doAssert(re"bc" in "abcd")
    -doAssert(re"(23)+" in "23232")
    -doAssert(re"^(23)+$" notin "23232")
    +search for the pattern anywhere in the string. It returns as soon as there is a match, even when the expression has repetitions. Use re"^regex$" to match the whole string instead of searching +

    Examples:

    +
    doAssert(re"bc" in "abcd")
    +doAssert(re"(23)+" in "23232")
    +doAssert(re"^(23)+$" notin "23232")
    @@ -1068,11 +1148,12 @@

    Procs

    tags: [].}
    -search through the string looking for the first location where there is a match
    var m: RegexMatch
    -doAssert "abcd".find(re"bc", m)
    -doAssert(not "abcd".find(re"de", m))
    -doAssert "2222".find(re"(22)*", m) and
    -  m.group(0) == @[0 .. 1, 2 .. 3]
    +search through the string looking for the first location where there is a match +

    Examples:

    +
    var m: RegexMatch
    +doAssert "abcd".find(re"bc", m)
    +doAssert(not "abcd".find(re"de", m))
    +doAssert("2222".find(re"(22)*", m) and m.group(0) == @[0 .. 1, 2 .. 3])
    @@ -1086,42 +1167,56 @@

    Procs

    proc findAndCaptureAll(s: string; pattern: Regex): seq[string] {...}{.raises: [], tags: [].}
    -search through the string and return a seq with captures.
    let
    -  expected = @["1", "2", "3", "4", "5"]
    -  res = findAndCaptureAll("a1b2c3d4e5", re"\d")
    -doAssert(res == expected)
    +search through the string and return a seq with captures. +

    Examples:

    +
    let
    +  captured = findAndCaptureAll("a1b2c3d4e5", re"\d")
    +  expected = @["1", "2", "3", "4", "5"]
    +doAssert captured == expected
    proc split(s: string; sep: Regex): seq[string] {...}{.raises: [], tags: [].}
    -return not matched substrings
    doAssert(split("11a22Ϊ33Ⓐ44弢55", re"\d+") ==
    -  @["", "a", "Ϊ", "Ⓐ", "弢", ""])
    +return not matched substrings +

    Examples:

    +
    let
    +  parts = split("11a22Ϊ33Ⓐ44弢55", re"\d+")
    +  expected = @["", "a", "Ϊ", "Ⓐ", "弢", ""]
    +doAssert parts == expected
    proc splitIncl(s: string; sep: Regex): seq[string] {...}{.raises: [], tags: [].}
    -return not matched substrings, including captured groups
    doAssert splitIncl("a,b", re"(,)") ==
    -  @["a", ",", "b"]
    +return not matched substrings, including captured groups +

    Examples:

    +
    let
    +  parts = splitIncl("a,b", re"(,)")
    +  expected = @["a", ",", "b"]
    +doAssert parts == expected
    proc startsWith(s: string; pattern: Regex; start = 0): bool {...}{.raises: [], tags: [].}
    -return whether the string starts with the pattern or not
    doAssert("abc".startsWith(re"\w"))
    -doAssert(not "abc".startsWith(re"\d"))
    +return whether the string starts with the pattern or not +

    Examples:

    +
    doAssert "abc".startsWith(re"\w")
    +doAssert(not "abc".startsWith(re"\d"))
    proc endsWith(s: string; pattern: Regex): bool {...}{.raises: [], tags: [].}
    -return whether the string ends with the pattern or not
    doAssert("abc".endsWith(re"\w"))
    -doAssert(not "abc".endsWith(re"\d"))
    +return whether the string ends with the pattern or not +

    Examples:

    +
    doAssert "abc".endsWith(re"\w")
    +doAssert(not "abc".endsWith(re"\d"))
    @@ -1132,11 +1227,12 @@

    Procs

    Replace matched substrings.

    Matched groups can be accessed with $N notation, where N is the group's index, starting at 1 (1-indexed). $$ means literal $.

    If limit is given, at most limit replacements are done. limit of 0 means there is no limit

    -
    doAssert("aaa".replace(re"a", "b", 1) == "baa")
    -doAssert("abc".replace(re"(a(b)c)", "m($1) m($2)") ==
    -  "m(abc) m(b)")
    -doAssert("Nim is awesome!".replace(re"(\w\B)", "$1_") ==
    -  "N_i_m i_s a_w_e_s_o_m_e!")
    + +

    Examples:

    +
    doAssert "aaa".replace(re"a", "b", 1) == "baa"
    +doAssert("abc".replace(re"(a(b)c)", "m($1) m($2)") == "m(abc) m(b)")
    +doAssert("Nim is awesome!".replace(re"(\w\B)", "$1_") ==
    +    "N_i_m i_s a_w_e_s_o_m_e!")
    @@ -1146,35 +1242,40 @@

    Procs

    Replace matched substrings.

    If limit is given, at most limit replacements are done. limit of 0 means there is no limit

    -
    proc removeEvenWords(m: RegexMatch, s: string): string =
    -  result = ""
    -  if m.group(1).len mod 2 != 0:
    -    result = s[m.group(0)[0]]
    +
    +

    Examples:

    +
    proc removeEvenWords(m: RegexMatch; s: string): string =
    +  result = ""
    +  if m.group(1).len mod 2 != 0:
    +    result = s[m.group(0)[0]]
     
     let
    -  text = "Es macht Spaß, alle geraden Wörter zu entfernen!"
    -  expected = "macht , geraden entfernen!"
    -doAssert(text.replace(re"((\w)+\s*)", removeEvenWords) == expected)
    + text = "Es macht Spaß, alle geraden Wörter zu entfernen!" + expected = "macht , geraden entfernen!" +doAssert text.replace(re"((\w)+\s*)", removeEvenWords) == expected
    proc toPattern(s: string): Regex {...}{.raises: [RegexError], tags: [].}
    -Parse and compile a regular expression. Deprecated: use directly re instead which works both at RT and CT.
    # compiled at run-time
    -let patternA = toPattern(r"aa?")
    -# compiled at compile-time
    -const patternB = toPattern(r"aa?")
    +Parse and compile a regular expression. Deprecated: use directly re instead which works both at RT and CT. +

    Examples:

    +
    let patternRt = toPattern(r"aa?")
    +const
    +  patternCt = toPattern(r"aa?")
    proc isInitialized(re: Regex): bool {...}{.raises: [], tags: [].}
    -Check whether the regex has been initialized
    var re: Regex
    -assert(not re.isInitialized)
    -re = re"foo"
    -assert re.isInitialized
    +Check whether the regex has been initialized +

    Examples:

    +
    var re: Regex
    +doAssert(not re.isInitialized)
    +re = re"foo"
    +doAssert re.isInitialized
    @@ -1200,30 +1301,30 @@

    Iterators

    iterator group(m: RegexMatch; i: int): Slice[int] {...}{.raises: [], tags: [].}
    -return slices for a given group. Slices of start > end are empty matches (i.e.: re"(\d?)") and they are included same as in PCRE.
    let
    -  expected = ["a", "b", "c"]
    -  text = "abc"
    -var m: RegexMatch
    -doAssert text.match(re"(\w)+", m)
    -var i = 0
    -for bounds in m.group(0):
    -  doAssert(expected[i] == text[bounds])
    -  inc i
    +return slices for a given group. Slices of start > end are empty matches (i.e.: re"(\d?)") and they are included same as in PCRE. +

    Examples:

    +
    let text = "abc"
    +var m: RegexMatch
    +doAssert text.match(re"(\w)+", m)
    +var captures = newSeq[string]()
    +for bounds in m.group(0):
    +  captures.add(text[bounds])
    +doAssert captures == @["a", "b", "c"]
    iterator group(m: RegexMatch; s: string): Slice[int] {...}{.raises: [KeyError], tags: [].}
    -return slices for a given named group
    let
    -  expected = ["a", "b", "c"]
    -  text = "abc"
    -var m: RegexMatch
    -doAssert text.match(re"(?P<foo>\w)+", m)
    -var i = 0
    -for bounds in m.group("foo"):
    -  doAssert(expected[i] == text[bounds])
    -  inc i
    +return slices for a given named group +

    Examples:

    +
    let text = "abc"
    +var m: RegexMatch
    +doAssert text.match(re"(?P<foo>\w)+", m)
    +var captures = newSeq[string]()
    +for bounds in m.group("foo"):
    +  captures.add(text[bounds])
    +doAssert captures == @["a", "b", "c"]
    @@ -1231,13 +1332,15 @@

    Iterators

    tags: [].}
    -search through the string and return each match. Empty matches (start > end) are included
    var i = 0
    -let
    -  expected = [1 .. 2, 4 .. 5]
    -  text = "abcabc"
    -for m in findAll(text, re"bc"):
    -  doAssert text[m.boundaries] == "bc"
    -  doAssert m.boundaries == expected[i]
    +search through the string and return each match. Empty matches (start > end) are included
    +

    Examples:

    +
    var
    +  expected = [1 .. 2, 4 .. 5]
    +  text = "abcabc"
    +  i = 0
    +for m in findAll(text, re"bc"):
    +  doAssert text[m.boundaries] == "bc"
    +  doAssert m.boundaries == expected[i]
       inc i
    @@ -1245,10 +1348,13 @@

    Iterators

    iterator split(s: string; sep: Regex): string {...}{.inline, raises: [], tags: [].}
    -return not matched substrings
    var i = 0
    -let expected = ["", "a", "Ϊ", "Ⓐ", "弢", ""]
    -for s in split("11a22Ϊ33Ⓐ44弢55", re"\d+"):
    -  doAssert(s == expected[i])
    +return not matched substrings
    +

    Examples:

    +
    var
    +  expected = ["", "a", "Ϊ", "Ⓐ", "弢", ""]
    +  i = 0
    +for s in split("11a22Ϊ33Ⓐ44弢55", re"\d+"):
    +  doAssert(s == expected[i])
       inc i
    @@ -1262,7 +1368,7 @@

    Iterators

    diff --git a/regex.nimble b/regex.nimble index 505aebf..7dfa775 100644 --- a/regex.nimble +++ b/regex.nimble @@ -1,6 +1,6 @@ # Package -version = "0.12.0" +version = "0.13.0" author = "Esteban Castro Borsani (@nitely)" description = "Linear time regex matching" license = "MIT"