Skip to content

Commit 93e9623

Browse files
committed
SI-7737 Regex matches Char
Enables Char extraction by regex. ``` val r = """(\p{Lower})""".r "cat"(0) match { case r(x) => true } val nc = """\p{Lower}""".r "cat"(0) match { case nc() => true } ```
1 parent e7876d5 commit 93e9623

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/library/scala/util/matching/Regex.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,44 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends
194194
else None
195195
}
196196

197+
/** Tries to match the String representation of a [[scala.Char]].
198+
* If the match succeeds, the result is the first matching
199+
* group if any groups are defined, or an empty Sequence otherwise.
200+
*
201+
* For example:
202+
*
203+
* {{{
204+
* val cat = "cat"
205+
* // the case must consume the group to match
206+
* val r = """(\p{Lower})""".r
207+
* cat(0) match { case r(x) => true }
208+
* cat(0) match { case r(_) => true }
209+
* cat(0) match { case r(_*) => true }
210+
* cat(0) match { case r() => true } // no match
211+
*
212+
* // there is no group to extract
213+
* val r = """\p{Lower}""".r
214+
* cat(0) match { case r(x) => true } // no match
215+
* cat(0) match { case r(_) => true } // no match
216+
* cat(0) match { case r(_*) => true } // matches
217+
* cat(0) match { case r() => true } // matches
218+
*
219+
* // even if there are multiple groups, only one is returned
220+
* val r = """((.))""".r
221+
* cat(0) match { case r(_) => true } // matches
222+
* cat(0) match { case r(_,_) => true } // no match
223+
* }}}
224+
*
225+
* @param c The Char to match
226+
* @return The match
227+
*/
228+
def unapplySeq(c: Char): Option[Seq[Char]] = {
229+
val m = pattern matcher c.toString
230+
if (runMatcher(m)) {
231+
if (m.groupCount > 0) Some(m group 1) else Some(Nil)
232+
} else None
233+
}
234+
197235
/** Tries to match on a [[scala.util.matching.Regex.Match]].
198236
* A previously failed match results in None.
199237
* If a successful match was made against the current pattern, then that result is used.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
package scala.util.matching
3+
4+
import org.junit.Assert._
5+
import org.junit.Test
6+
import org.junit.runner.RunWith
7+
import org.junit.runners.JUnit4
8+
9+
import PartialFunction._
10+
11+
/** Regex can match a Char.
12+
* If the pattern includes a group,
13+
* always return a single char.
14+
*/
15+
@RunWith(classOf[JUnit4])
16+
class CharRegexTest {
17+
implicit class Averrable(val b: Boolean) /*extends AnyVal*/ {
18+
def yes = assert(b)
19+
def no = assert(!b)
20+
}
21+
val c: Char = 'c' // "cat"(0)
22+
val d: Char = 'D' // "Dog"(0)
23+
24+
@Test def comparesGroupCorrectly(): Unit = {
25+
val r = """(\p{Lower})""".r
26+
cond(c) { case r(x) => true } .yes
27+
cond(c) { case r(_) => true } .yes
28+
cond(c) { case r(_*) => true } .yes
29+
cond(c) { case r() => true } .no
30+
31+
cond(d) { case r(x) => true } .no
32+
cond(d) { case r(_) => true } .no
33+
cond(d) { case r(_*) => true } .no
34+
cond(d) { case r() => true } .no
35+
}
36+
37+
@Test def comparesNoGroupCorrectly(): Unit = {
38+
val rnc = """\p{Lower}""".r
39+
cond(c) { case rnc(x) => true } .no
40+
cond(c) { case rnc(_) => true } .no
41+
cond(c) { case rnc(_*) => true } .yes
42+
cond(c) { case rnc() => true } .yes
43+
44+
cond(d) { case rnc(x) => true } .no
45+
cond(d) { case rnc(_) => true } .no
46+
cond(d) { case rnc(_*) => true } .no
47+
cond(d) { case rnc() => true } .no
48+
}
49+
50+
@Test(expected = classOf[MatchError])
51+
def failCorrectly(): Unit = {
52+
val headAndTail = """(\p{Lower})([a-z]+)""".r
53+
val n = "cat"(0) match {
54+
case headAndTail(ht @ _*) => ht.size
55+
}
56+
assert(false, s"Match size $n")
57+
}
58+
}

0 commit comments

Comments
 (0)