diff --git a/backend.native/tests/harmony_regex/MatchResultTest.kt b/backend.native/tests/harmony_regex/MatchResultTest.kt index 7c3bf53cde0..01af9e16946 100644 --- a/backend.native/tests/harmony_regex/MatchResultTest.kt +++ b/backend.native/tests/harmony_regex/MatchResultTest.kt @@ -435,6 +435,29 @@ class MatchResultTest { assertFalse(regex.containsMatchIn(str)) } + /** + * Regression test for https://github.com/JetBrains/kotlin-native/issues/2297 + */ + @Test fun test2297() { + assertTrue(Regex("^(:[0-5]?[0-9])+$").matches(":20:30")) + assertTrue(Regex("(.{1,}){2}").matches("aa")) + + assertTrue(Regex("(.+b)+").matches("0b0b")) + assertTrue(Regex("(.+?b)+").matches("0b0b")) + assertTrue(Regex("(.?b)+").matches("0b0b")) + assertTrue(Regex("(.??b)+").matches("0b0b")) + assertTrue(Regex("(.*b)+").matches("0b0b")) + assertTrue(Regex("(.*?b)+").matches("0b0b")) + assertTrue(Regex("(.{1,2}b)+").matches("0b00b")) + assertTrue(Regex("(.{1,2}?b)+").matches("0b00b")) + + assertTrue(Regex("([0]?[0]?)+").matches("0000")) + assertTrue(Regex("([0]?[0]?b)+").matches("00b00b")) + assertTrue(Regex("((b{2}){3})+").matches("bbbbbbbbbbbb")) + + assertTrue(Regex("[^a]").matches("b")) + } + /** * Regression test for HARMONY-3360 */ diff --git a/runtime/src/main/kotlin/kotlin/text/regex/sets/GroupQuantifierSet.kt b/runtime/src/main/kotlin/kotlin/text/regex/sets/GroupQuantifierSet.kt index 7d7b2f37a81..95fecf1149c 100644 --- a/runtime/src/main/kotlin/kotlin/text/regex/sets/GroupQuantifierSet.kt +++ b/runtime/src/main/kotlin/kotlin/text/regex/sets/GroupQuantifierSet.kt @@ -42,44 +42,47 @@ open internal class GroupQuantifierSet( // We call innerSet.matches here, if it succeeds it call next.matches where next is this QuantifierSet. // So we have a recursive searching procedure. override fun matches(startIndex: Int, testString: CharSequence, matchResult: MatchResultImpl): Int { + var enterCount = matchResult.enterCounters[groupQuantifierIndex] + + fun matchNext(): Int { + matchResult.enterCounters[groupQuantifierIndex] = 0 + val result = next.matches(startIndex, testString, matchResult) + matchResult.enterCounters[groupQuantifierIndex] = enterCount + return result + } if (!innerSet.hasConsumed(matchResult)) { - return next.matches(startIndex, testString, matchResult) + return matchNext() } // Fast case: '*' or {0, } - no need to count occurrences. if (min == 0 && max == Quantifier.INF) { val nextIndex = innerSet.matches(startIndex, testString, matchResult) - return if (nextIndex < 0) { - next.matches(startIndex, testString, matchResult) + matchNext() } else { nextIndex } } - val enterCount = matchResult.enterCounters[groupQuantifierIndex] - // can't go inner set; if (max != Quantifier.INF && enterCount >= max) { - return next.matches(startIndex, testString, matchResult) + return matchNext() } // go inner set; - matchResult.enterCounters[groupQuantifierIndex]++ + matchResult.enterCounters[groupQuantifierIndex] = ++enterCount val nextIndex = innerSet.matches(startIndex, testString, matchResult) - if (nextIndex < 0) { - matchResult.enterCounters[groupQuantifierIndex]-- + return if (nextIndex < 0) { + matchResult.enterCounters[groupQuantifierIndex] = --enterCount if (enterCount >= min) { - return next.matches(startIndex, testString, matchResult) + matchNext() } else { - matchResult.enterCounters[groupQuantifierIndex] = 0 - return -1 + -1 } } else { - matchResult.enterCounters[groupQuantifierIndex] = 0 - return nextIndex + nextIndex } } diff --git a/runtime/src/main/kotlin/kotlin/text/regex/sets/ReluctantGroupQuantifierSet.kt b/runtime/src/main/kotlin/kotlin/text/regex/sets/ReluctantGroupQuantifierSet.kt index e492d04d681..a6da1602cd2 100644 --- a/runtime/src/main/kotlin/kotlin/text/regex/sets/ReluctantGroupQuantifierSet.kt +++ b/runtime/src/main/kotlin/kotlin/text/regex/sets/ReluctantGroupQuantifierSet.kt @@ -34,9 +34,17 @@ internal class ReluctantGroupQuantifierSet( ) : GroupQuantifierSet(quantifier, innerSet, next, type, setCounter) { override fun matches(startIndex: Int, testString: CharSequence, matchResult: MatchResultImpl): Int { + var enterCount = matchResult.enterCounters[groupQuantifierIndex] + + fun matchNext(): Int { + matchResult.enterCounters[groupQuantifierIndex] = 0 + val result = next.matches(startIndex, testString, matchResult) + matchResult.enterCounters[groupQuantifierIndex] = enterCount + return result + } if (!innerSet.hasConsumed(matchResult)) { - return next.matches(startIndex, testString, matchResult) + return matchNext() } // Fast case: '*' or {0, } - no need to count occurrences. @@ -49,29 +57,22 @@ internal class ReluctantGroupQuantifierSet( } } - val enterCounter = matchResult.enterCounters[groupQuantifierIndex] - // can't go inner set; - if (enterCounter >= max) { - matchResult.enterCounters[groupQuantifierIndex] = 0 - return next.matches(startIndex, testString, matchResult) + if (max != Quantifier.INF && enterCount >= max) { + return matchNext() } - var nextIndex: Int - if (enterCounter >= min) { - nextIndex = next.matches(startIndex, testString, matchResult) + return if (enterCount >= min) { + val nextIndex = matchNext() if (nextIndex < 0) { - matchResult.enterCounters[groupQuantifierIndex]++ - nextIndex = innerSet.matches(startIndex, testString, matchResult) + matchResult.enterCounters[groupQuantifierIndex] = ++enterCount + innerSet.matches(startIndex, testString, matchResult) } else { - matchResult.enterCounters[groupQuantifierIndex] = 0 - return nextIndex + nextIndex } } else { - matchResult.enterCounters[groupQuantifierIndex]++ - nextIndex = innerSet.matches(startIndex, testString, matchResult) + matchResult.enterCounters[groupQuantifierIndex] = ++enterCount + innerSet.matches(startIndex, testString, matchResult) } - - return nextIndex } }