Pattern implementation for JS.
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package kotlin.text
|
package kotlin.text
|
||||||
|
|
||||||
|
private val TODO: Nothing get() = throw java.lang.UnsupportedOperationException()
|
||||||
|
|
||||||
|
|
||||||
public enum class PatternOption(val value: String) {
|
public enum class PatternOption(val value: String) {
|
||||||
IGNORE_CASE : PatternOption("i")
|
IGNORE_CASE : PatternOption("i")
|
||||||
@@ -25,24 +27,11 @@ public enum class PatternOption(val value: String) {
|
|||||||
|
|
||||||
public data class MatchGroup(val value: String)
|
public data class MatchGroup(val value: String)
|
||||||
|
|
||||||
public trait MatchGroupCollection : Collection<MatchGroup?> {
|
|
||||||
public fun get(index: Int): MatchGroup?
|
|
||||||
}
|
|
||||||
|
|
||||||
public trait MatchResult {
|
public class Pattern (pattern: String, options: Set<PatternOption>) {
|
||||||
public val range: IntRange
|
|
||||||
public val value: String
|
|
||||||
public val groups: MatchGroupCollection
|
|
||||||
|
|
||||||
public fun next(): MatchResult?
|
public val pattern: String = pattern
|
||||||
}
|
public val options: Set<PatternOption> = options.toSet()
|
||||||
|
|
||||||
|
|
||||||
public class Pattern(public val pattern: String, options_: Set<PatternOption>) {
|
|
||||||
|
|
||||||
public constructor(pattern: String, vararg options: PatternOption) : this(pattern, options.toSet())
|
|
||||||
|
|
||||||
public val options: Set<PatternOption> = options_.toSet()
|
|
||||||
private val nativePattern: RegExp = RegExp(pattern, options.map { it.value }.joinToString() + "g")
|
private val nativePattern: RegExp = RegExp(pattern, options.map { it.value }.joinToString() + "g")
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +49,7 @@ public class Pattern(public val pattern: String, options_: Set<PatternOption>) {
|
|||||||
|
|
||||||
public fun split(input: CharSequence, limit: Int = 0): List<String> = TODO
|
public fun split(input: CharSequence, limit: Int = 0): List<String> = TODO
|
||||||
|
|
||||||
public fun toString(): String = nativePattern.toString()
|
public override fun toString(): String = nativePattern.toString()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
public fun fromLiteral(literal: String): Pattern = Pattern(escape(literal))
|
public fun fromLiteral(literal: String): Pattern = Pattern(escape(literal))
|
||||||
@@ -69,6 +58,8 @@ public class Pattern(public val pattern: String, options_: Set<PatternOption>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun Pattern(pattern: String, vararg options: PatternOption): Pattern = Pattern(pattern, options.toSet())
|
||||||
|
|
||||||
|
|
||||||
private fun RegExp.findNext(input: String, from: Int): MatchResult? {
|
private fun RegExp.findNext(input: String, from: Int): MatchResult? {
|
||||||
this.lastIndex = from
|
this.lastIndex = from
|
||||||
|
|||||||
@@ -19,17 +19,19 @@ public fun String.nativeStartsWith(s: String, position: Int): Boolean = noImpl
|
|||||||
native("endsWith")
|
native("endsWith")
|
||||||
public fun String.nativeEndsWith(s: String): Boolean = noImpl
|
public fun String.nativeEndsWith(s: String): Boolean = noImpl
|
||||||
|
|
||||||
|
deprecated("Use split(Pattern) instead.")
|
||||||
library("splitString")
|
library("splitString")
|
||||||
public fun String.splitWithRegex(regex: String): Array<String> = noImpl
|
public fun String.splitWithRegex(regex: String): Array<String> = noImpl
|
||||||
|
|
||||||
|
deprecated("Use split(Pattern) instead.")
|
||||||
library("splitString")
|
library("splitString")
|
||||||
public fun String.splitWithRegex(regex: String, limit: Int): Array<String> = noImpl
|
public fun String.splitWithRegex(regex: String, limit: Int): Array<String> = noImpl
|
||||||
|
|
||||||
deprecated("Use splitWithRegex (temporary)")
|
deprecated("Use split(Pattern) instead.")
|
||||||
library("splitString")
|
library("splitString")
|
||||||
public fun String.split(regex: String): Array<String> = noImpl
|
public fun String.split(regex: String): Array<String> = noImpl
|
||||||
|
|
||||||
deprecated("Use splitWithRegex (temporary)")
|
deprecated("Use split(Pattern) instead.")
|
||||||
library("splitString")
|
library("splitString")
|
||||||
public fun String.split(regex: String, limit: Int): Array<String> = noImpl
|
public fun String.split(regex: String, limit: Int): Array<String> = noImpl
|
||||||
|
|
||||||
@@ -52,12 +54,13 @@ public fun CharSequence.isEmpty(): Boolean = noImpl
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: internal
|
||||||
// native because we need to escape newValue
|
// native because we need to escape newValue
|
||||||
|
//native("replace")
|
||||||
|
//public fun String.nativeReplace(oldValue: String, newValue: String): String = noImpl
|
||||||
|
// TODO: internal
|
||||||
native("replace")
|
native("replace")
|
||||||
private fun String.nativeReplace(oldValue: String, newValue: String): String = noImpl
|
public fun String.nativeReplace(pattern: RegExp, replacement: String): String = noImpl
|
||||||
|
|
||||||
native("replace")
|
|
||||||
private fun String.nativeReplace(pattern: RegExp, replacement: String): String = noImpl
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ public inline fun String.decapitalize(): String {
|
|||||||
|
|
||||||
|
|
||||||
public fun String.replace(oldValue: String, newValue: String): String =
|
public fun String.replace(oldValue: String, newValue: String): String =
|
||||||
nativeReplace(new RegExp(Pattern.escape(oldValue),"g"), Pattern.escapeReplacement(newValue))
|
nativeReplace(RegExp(kotlin.text.Pattern.escape(oldValue),"g"), kotlin.text.Pattern.escapeReplacement(newValue))
|
||||||
|
|
||||||
public fun String.replace(oldChar: Char, newChar: Char): String =
|
public fun String.replace(oldChar: Char, newChar: Char): String =
|
||||||
nativeReplace(new RegExp(Pattern.escape(oldChar.toString()),"g"), newChar.toString())
|
nativeReplace(RegExp(kotlin.text.Pattern.escape(oldChar.toString()),"g"), newChar.toString())
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class RegExpTest {
|
|||||||
|
|
||||||
val m2 = re.exec(string)!!
|
val m2 = re.exec(string)!!
|
||||||
assertEquals(array("A5D5", "A5", "D5"), m2)
|
assertEquals(array("A5D5", "A5", "D5"), m2)
|
||||||
assertEquals(string.indexOf(m2[0]), (m2 as RegExpMatch).index)
|
assertEquals(string.indexOf(m2[0]!!), (m2 as RegExpMatch).index)
|
||||||
|
|
||||||
val noMatch = re.exec(string)
|
val noMatch = re.exec(string)
|
||||||
assertEquals(null, noMatch)
|
assertEquals(null, noMatch)
|
||||||
|
|||||||
@@ -16,45 +16,6 @@
|
|||||||
|
|
||||||
package kotlin.text
|
package kotlin.text
|
||||||
|
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.regex.Pattern as NativePattern
|
|
||||||
import java.util.regex.Matcher
|
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
private val TODO: Nothing get() = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
public trait FlagEnum {
|
|
||||||
public val value: Int
|
|
||||||
public val mask: Int
|
|
||||||
}
|
|
||||||
public fun Iterable<FlagEnum>.toInt(): Int =
|
|
||||||
this.fold(0, { value, option -> value or option.value })
|
|
||||||
public fun <T: FlagEnum> fromInt(value: Int, allValues: Array<T>): Set<T> =
|
|
||||||
allValues.filter({ value and it.mask == it.value }).toSet()
|
|
||||||
|
|
||||||
|
|
||||||
public enum class PatternOption(override val value: Int, override val mask: Int = value) : FlagEnum {
|
|
||||||
// common
|
|
||||||
IGNORE_CASE : PatternOption(NativePattern.CASE_INSENSITIVE)
|
|
||||||
MULTILINE : PatternOption(NativePattern.MULTILINE)
|
|
||||||
|
|
||||||
//jvm-specific
|
|
||||||
LITERAL : PatternOption(NativePattern.LITERAL)
|
|
||||||
UNICODE_CASE: PatternOption(NativePattern.UNICODE_CASE)
|
|
||||||
UNIX_LINES: PatternOption(NativePattern.UNIX_LINES)
|
|
||||||
COMMENTS: PatternOption(NativePattern.COMMENTS)
|
|
||||||
DOT_MATCHES_ALL: PatternOption(NativePattern.DOTALL)
|
|
||||||
CANON_EQ: PatternOption(NativePattern.CANON_EQ)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* in JS
|
|
||||||
public enum class PatternOptions(val value: String) {
|
|
||||||
IGNORE_CASE : PatternOptions("i")
|
|
||||||
MULTILINE : PatternOptions("m")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public data class MatchGroup(val value: String, val range: IntRange)
|
|
||||||
|
|
||||||
public trait MatchGroupCollection : Collection<MatchGroup?> {
|
public trait MatchGroupCollection : Collection<MatchGroup?> {
|
||||||
public fun get(index: Int): MatchGroup?
|
public fun get(index: Int): MatchGroup?
|
||||||
@@ -69,81 +30,7 @@ public trait MatchResult {
|
|||||||
public fun next(): MatchResult?
|
public fun next(): MatchResult?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maybe move them to kotlin package to be more discoverable
|
||||||
public class Pattern ( /* visibility? */ val nativePattern: NativePattern) {
|
|
||||||
|
|
||||||
public constructor(pattern: String, options: Set<PatternOption>): this(NativePattern.compile(pattern, options.toInt()))
|
|
||||||
public constructor(pattern: String, vararg options: PatternOption) : this(pattern, options.toSet())
|
|
||||||
|
|
||||||
public val pattern: String
|
|
||||||
get() = nativePattern.pattern()
|
|
||||||
|
|
||||||
public val options: Set<PatternOption> = fromInt(nativePattern.flags(), PatternOption.values())
|
|
||||||
|
|
||||||
public fun matches(input: CharSequence): Boolean = nativePattern.matcher(input).matches()
|
|
||||||
|
|
||||||
public fun match(input: CharSequence): MatchResult? = nativePattern.matcher(input).findNext(0)
|
|
||||||
|
|
||||||
public fun matchAll(input: CharSequence): Sequence<MatchResult> = sequence({ match(input) }, { match -> match.next() })
|
|
||||||
|
|
||||||
public fun replace(input: CharSequence, replacement: String): String = nativePattern.matcher(input).replaceAll(replacement)
|
|
||||||
public fun replace(input: CharSequence, evaluator: (MatchResult) -> String): String = TODO
|
|
||||||
|
|
||||||
public fun split(input: CharSequence, limit: Int = 0): List<String> = nativePattern.split(input, limit).asList() // TODO: require(limit>=0)
|
|
||||||
|
|
||||||
public override fun toString(): String = nativePattern.toString()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
public fun fromLiteral(literal: String): Pattern = Pattern(literal, PatternOption.LITERAL)
|
|
||||||
public fun escape(literal: String): String = NativePattern.quote(literal)
|
|
||||||
public fun escapeReplacement(literal: String): String = Matcher.quoteReplacement(literal)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun String.toPattern(vararg options: PatternOption): Pattern = Pattern(this, *options)
|
public fun String.toPattern(vararg options: PatternOption): Pattern = Pattern(this, *options)
|
||||||
public fun String.toPattern(options: Set<PatternOption>): Pattern = Pattern(this, options)
|
public fun String.toPattern(options: Set<PatternOption>): Pattern = Pattern(this, options)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun Matcher.findNext(from: Int): MatchResult? {
|
|
||||||
if (!find(from))
|
|
||||||
return null
|
|
||||||
|
|
||||||
// TODO: If we need MatchResult to be thread safe we must lock everything, or call this.toMatchResult early
|
|
||||||
var matchResult: java.util.regex.MatchResult = this
|
|
||||||
|
|
||||||
return object: MatchResult {
|
|
||||||
override val range: IntRange
|
|
||||||
get() = matchResult.range()
|
|
||||||
override val value: String
|
|
||||||
get() = matchResult.group()
|
|
||||||
|
|
||||||
override val groups: MatchGroupCollection = object : MatchGroupCollection {
|
|
||||||
override fun size(): Int = matchResult.groupCount() + 1
|
|
||||||
override fun isEmpty(): Boolean = false
|
|
||||||
override fun contains(o: Any?): Boolean = o is MatchGroup? && this.any({ it == o })
|
|
||||||
override fun containsAll(c: Collection<Any?>): Boolean = c.all({contains(it)})
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<MatchGroup?> = indices.sequence().map { this[it] }.iterator()
|
|
||||||
override fun get(index: Int): MatchGroup? {
|
|
||||||
val range = matchResult.range(index)
|
|
||||||
return if (range.start >= 0)
|
|
||||||
MatchGroup(matchResult.group(index), range)
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun next(): MatchResult? {
|
|
||||||
if (this@findNext === matchResult)
|
|
||||||
matchResult = this@findNext.toMatchResult()
|
|
||||||
return this@findNext.findNext(matchResult.end())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun java.util.regex.MatchResult.range(): IntRange = start()..end()-1
|
|
||||||
private fun java.util.regex.MatchResult.range(groupIndex: Int): IntRange = start(groupIndex)..end(groupIndex)-1
|
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.text
|
||||||
|
|
||||||
|
import java.util.regex.Pattern as NativePattern
|
||||||
|
import java.util.regex.Matcher
|
||||||
|
|
||||||
|
private val TODO: Nothing get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
public trait FlagEnum {
|
||||||
|
public val value: Int
|
||||||
|
public val mask: Int
|
||||||
|
}
|
||||||
|
public fun Iterable<FlagEnum>.toInt(): Int =
|
||||||
|
this.fold(0, { value, option -> value or option.value })
|
||||||
|
public fun <T: FlagEnum> fromInt(value: Int, allValues: Array<T>): Set<T> =
|
||||||
|
allValues.filter({ value and it.mask == it.value }).toSet()
|
||||||
|
|
||||||
|
|
||||||
|
public enum class PatternOption(override val value: Int, override val mask: Int = value) : FlagEnum {
|
||||||
|
// common
|
||||||
|
IGNORE_CASE : PatternOption(NativePattern.CASE_INSENSITIVE)
|
||||||
|
MULTILINE : PatternOption(NativePattern.MULTILINE)
|
||||||
|
|
||||||
|
//jvm-specific
|
||||||
|
LITERAL : PatternOption(NativePattern.LITERAL)
|
||||||
|
UNICODE_CASE: PatternOption(NativePattern.UNICODE_CASE)
|
||||||
|
UNIX_LINES: PatternOption(NativePattern.UNIX_LINES)
|
||||||
|
COMMENTS: PatternOption(NativePattern.COMMENTS)
|
||||||
|
DOT_MATCHES_ALL: PatternOption(NativePattern.DOTALL)
|
||||||
|
CANON_EQ: PatternOption(NativePattern.CANON_EQ)
|
||||||
|
}
|
||||||
|
|
||||||
|
public data class MatchGroup(val value: String, val range: IntRange)
|
||||||
|
|
||||||
|
|
||||||
|
public class Pattern ( /* visibility? */ val nativePattern: NativePattern) {
|
||||||
|
|
||||||
|
public constructor(pattern: String, options: Set<PatternOption>): this(NativePattern.compile(pattern, options.toInt()))
|
||||||
|
public constructor(pattern: String, vararg options: PatternOption) : this(pattern, options.toSet())
|
||||||
|
|
||||||
|
public val pattern: String
|
||||||
|
get() = nativePattern.pattern()
|
||||||
|
|
||||||
|
public val options: Set<PatternOption> = fromInt(nativePattern.flags(), PatternOption.values())
|
||||||
|
|
||||||
|
public fun matches(input: CharSequence): Boolean = nativePattern.matcher(input).matches()
|
||||||
|
|
||||||
|
public fun match(input: CharSequence): MatchResult? = nativePattern.matcher(input).findNext(0)
|
||||||
|
|
||||||
|
public fun matchAll(input: CharSequence): Sequence<MatchResult> = sequence({ match(input) }, { match -> match.next() })
|
||||||
|
|
||||||
|
public fun replace(input: CharSequence, replacement: String): String = nativePattern.matcher(input).replaceAll(replacement)
|
||||||
|
public fun replace(input: CharSequence, evaluator: (MatchResult) -> String): String = TODO
|
||||||
|
|
||||||
|
public fun split(input: CharSequence, limit: Int = 0): List<String> = nativePattern.split(input, limit).asList() // TODO: require(limit>=0)
|
||||||
|
|
||||||
|
public override fun toString(): String = nativePattern.toString()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
public fun fromLiteral(literal: String): Pattern = Pattern(literal, PatternOption.LITERAL)
|
||||||
|
public fun escape(literal: String): String = NativePattern.quote(literal)
|
||||||
|
public fun escapeReplacement(literal: String): String = Matcher.quoteReplacement(literal)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Matcher.findNext(from: Int): MatchResult? {
|
||||||
|
if (!find(from))
|
||||||
|
return null
|
||||||
|
|
||||||
|
// TODO: If we need MatchResult to be thread safe we must lock everything, or call this.toMatchResult early
|
||||||
|
var matchResult: java.util.regex.MatchResult = this
|
||||||
|
|
||||||
|
return object: MatchResult {
|
||||||
|
override val range: IntRange
|
||||||
|
get() = matchResult.range()
|
||||||
|
override val value: String
|
||||||
|
get() = matchResult.group()
|
||||||
|
|
||||||
|
override val groups: MatchGroupCollection = object : MatchGroupCollection {
|
||||||
|
override fun size(): Int = matchResult.groupCount() + 1
|
||||||
|
override fun isEmpty(): Boolean = false
|
||||||
|
override fun contains(o: Any?): Boolean = o is MatchGroup? && this.any({ it == o })
|
||||||
|
override fun containsAll(c: Collection<Any?>): Boolean = c.all({contains(it)})
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<MatchGroup?> = indices.sequence().map { this[it] }.iterator()
|
||||||
|
override fun get(index: Int): MatchGroup? {
|
||||||
|
val range = matchResult.range(index)
|
||||||
|
return if (range.start >= 0)
|
||||||
|
MatchGroup(matchResult.group(index), range)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun next(): MatchResult? {
|
||||||
|
if (this@findNext === matchResult)
|
||||||
|
matchResult = this@findNext.toMatchResult()
|
||||||
|
return this@findNext.findNext(matchResult.end())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun java.util.regex.MatchResult.range(): IntRange = start()..end()-1
|
||||||
|
private fun java.util.regex.MatchResult.range(groupIndex: Int): IntRange = start(groupIndex)..end(groupIndex)-1
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package test.text
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
import kotlin.text.*
|
||||||
|
import org.junit.Test as test
|
||||||
|
|
||||||
|
|
||||||
|
class PatternJVMTest {
|
||||||
|
|
||||||
|
test fun matchGroups() {
|
||||||
|
val input = "1a 2b 3c"
|
||||||
|
val pattern = "(\\d)(\\w)".toPattern()
|
||||||
|
|
||||||
|
val matches = pattern.matchAll(input).toList()
|
||||||
|
assertTrue(matches.all { it.groups.size() == 3 })
|
||||||
|
val m1 = matches[0]
|
||||||
|
assertEquals("1a", m1.groups[0]?.value)
|
||||||
|
assertEquals(0..1, m1.groups[0]?.range)
|
||||||
|
assertEquals("1", m1.groups[1]?.value)
|
||||||
|
assertEquals(0..0, m1.groups[1]?.range)
|
||||||
|
assertEquals("a", m1.groups[2]?.value)
|
||||||
|
assertEquals(1..1, m1.groups[2]?.range)
|
||||||
|
|
||||||
|
val m2 = matches[1]
|
||||||
|
assertEquals("2", m2.groups[1]?.value)
|
||||||
|
assertEquals(3..3, m2.groups[1]?.range)
|
||||||
|
assertEquals("b", m2.groups[2]?.value)
|
||||||
|
assertEquals(4..4, m2.groups[2]?.range)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package test.text
|
package test.text
|
||||||
|
|
||||||
import kotlin.test.*
|
|
||||||
import kotlin.text.*
|
import kotlin.text.*
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
import org.junit.Test as test
|
import org.junit.Test as test
|
||||||
|
|
||||||
class PatternTest {
|
class PatternTest {
|
||||||
@@ -48,17 +49,12 @@ class PatternTest {
|
|||||||
assertTrue(matches.all { it.groups.size() == 3 })
|
assertTrue(matches.all { it.groups.size() == 3 })
|
||||||
val m1 = matches[0]
|
val m1 = matches[0]
|
||||||
assertEquals("1a", m1.groups[0]?.value)
|
assertEquals("1a", m1.groups[0]?.value)
|
||||||
assertEquals(0..1, m1.groups[0]?.range)
|
|
||||||
assertEquals("1", m1.groups[1]?.value)
|
assertEquals("1", m1.groups[1]?.value)
|
||||||
assertEquals(0..0, m1.groups[1]?.range)
|
|
||||||
assertEquals("a", m1.groups[2]?.value)
|
assertEquals("a", m1.groups[2]?.value)
|
||||||
assertEquals(1..1, m1.groups[2]?.range)
|
|
||||||
|
|
||||||
val m2 = matches[1]
|
val m2 = matches[1]
|
||||||
assertEquals("2", m2.groups[1]?.value)
|
assertEquals("2", m2.groups[1]?.value)
|
||||||
assertEquals(3..3, m2.groups[1]?.range)
|
|
||||||
assertEquals("b", m2.groups[2]?.value)
|
assertEquals("b", m2.groups[2]?.value)
|
||||||
assertEquals(4..4, m2.groups[2]?.range)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test fun matchOptionalGroup() {
|
test fun matchOptionalGroup() {
|
||||||
|
|||||||
Reference in New Issue
Block a user