Equivalize isLowerCase and isUpperCase behavior in all platforms #KT-46184

This commit is contained in:
Abduqodiri Qurbonzoda
2021-05-25 05:32:29 +03:00
parent 0b10f255d7
commit d934c97bf5
25 changed files with 386 additions and 75 deletions
@@ -68,17 +68,17 @@ internal fun Char.isLetterImpl(): Boolean {
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is a lower case letter, or it has contributory property Other_Lowercase.
*/
internal fun Char.isLowerCaseImpl(): Boolean {
return getLetterType() == 1
return getLetterType() == 1 || code.isOtherLowercase()
}
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is an upper case letter, or it has contributory property Other_Uppercase.
*/
internal fun Char.isUpperCaseImpl(): Boolean {
return getLetterType() == 2
return getLetterType() == 2 || code.isOtherUppercase()
}
/**
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
@SharedImmutable
private val otherLowerStart = intArrayOf(
0x00aa, 0x00ba, 0x02b0, 0x02c0, 0x02e0, 0x0345, 0x037a, 0x1d2c, 0x1d78, 0x1d9b, 0x2071, 0x207f, 0x2090, 0x2170, 0x24d0, 0x2c7c, 0xa69c, 0xa770, 0xa7f8, 0xab5c,
)
@SharedImmutable
private val otherLowerLength = intArrayOf(
1, 1, 9, 2, 5, 1, 1, 63, 1, 37, 1, 1, 13, 16, 26, 2, 2, 1, 2, 4,
)
internal fun Int.isOtherLowercase(): Boolean {
val index = binarySearchRange(otherLowerStart, this)
return index >= 0 && this < otherLowerStart[index] + otherLowerLength[index]
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
internal fun Int.isOtherUppercase(): Boolean {
return this in 0x2160..0x216f
|| this in 0x24b6..0x24cf
|| this in 0x1f130..0x1f149
|| this in 0x1f150..0x1f169
|| this in 0x1f170..0x1f189
}
@@ -12,17 +12,13 @@ package kotlin.text
@SharedImmutable
private val casedStart = intArrayOf(
0x00aa, 0x00ba, 0x02b0, 0x02c0, 0x02e0, 0x0345, 0x037a, 0x1d2c, 0x1d78, 0x1d9b, 0x2071, 0x207f, 0x2090, 0x2160, 0x2170, 0x24b6, 0x24d0, 0x2c7c, 0xa69c, 0xa770,
0xa7f8, 0xab5c, 0x10400, 0x104b0, 0x104d8, 0x10c80, 0x10cc0, 0x118a0, 0x16e40, 0x1d400, 0x1d456, 0x1d49e, 0x1d4a2, 0x1d4a5, 0x1d4a9, 0x1d4ae, 0x1d4bb, 0x1d4bd, 0x1d4c5, 0x1d507,
0x1d50d, 0x1d516, 0x1d51e, 0x1d53b, 0x1d540, 0x1d546, 0x1d54a, 0x1d552, 0x1d6a8, 0x1d6c2, 0x1d6dc, 0x1d6fc, 0x1d716, 0x1d736, 0x1d750, 0x1d770, 0x1d78a, 0x1d7aa, 0x1d7c4, 0x1e900,
0x1f130, 0x1f150, 0x1f170,
0x10400, 0x104b0, 0x104d8, 0x10c80, 0x10cc0, 0x118a0, 0x16e40, 0x1d400, 0x1d456, 0x1d49e, 0x1d4a2, 0x1d4a5, 0x1d4a9, 0x1d4ae, 0x1d4bb, 0x1d4bd, 0x1d4c5, 0x1d507, 0x1d50d, 0x1d516,
0x1d51e, 0x1d53b, 0x1d540, 0x1d546, 0x1d54a, 0x1d552, 0x1d6a8, 0x1d6c2, 0x1d6dc, 0x1d6fc, 0x1d716, 0x1d736, 0x1d750, 0x1d770, 0x1d78a, 0x1d7aa, 0x1d7c4, 0x1e900,
)
@SharedImmutable
private val casedEnd = intArrayOf(
0x00aa, 0x00ba, 0x02b8, 0x02c1, 0x02e4, 0x0345, 0x037a, 0x1d6a, 0x1d78, 0x1dbf, 0x2071, 0x207f, 0x209c, 0x216f, 0x217f, 0x24cf, 0x24e9, 0x2c7d, 0xa69d, 0xa770,
0xa7f9, 0xab5f, 0x1044f, 0x104d3, 0x104fb, 0x10cb2, 0x10cf2, 0x118df, 0x16e7f, 0x1d454, 0x1d49c, 0x1d49f, 0x1d4a2, 0x1d4a6, 0x1d4ac, 0x1d4b9, 0x1d4bb, 0x1d4c3, 0x1d505, 0x1d50a,
0x1d514, 0x1d51c, 0x1d539, 0x1d53e, 0x1d544, 0x1d546, 0x1d550, 0x1d6a5, 0x1d6c0, 0x1d6da, 0x1d6fa, 0x1d714, 0x1d734, 0x1d74e, 0x1d76e, 0x1d788, 0x1d7a8, 0x1d7c2, 0x1d7cb, 0x1e943,
0x1f149, 0x1f169, 0x1f189,
0x1044f, 0x104d3, 0x104fb, 0x10cb2, 0x10cf2, 0x118df, 0x16e7f, 0x1d454, 0x1d49c, 0x1d49f, 0x1d4a2, 0x1d4a6, 0x1d4ac, 0x1d4b9, 0x1d4bb, 0x1d4c3, 0x1d505, 0x1d50a, 0x1d514, 0x1d51c,
0x1d539, 0x1d53e, 0x1d544, 0x1d546, 0x1d550, 0x1d6a5, 0x1d6c0, 0x1d6da, 0x1d6fa, 0x1d714, 0x1d734, 0x1d74e, 0x1d76e, 0x1d788, 0x1d7a8, 0x1d7c2, 0x1d7cb, 0x1e943,
)
// Lu + Ll + Lt + Other_Lowercase + Other_Uppercase (PropList.txt of Unicode Character Database files)
@@ -35,6 +31,9 @@ internal fun Int.isCased(): Boolean {
CharCategory.TITLECASE_LETTER.value -> return true
}
}
if (isOtherUppercase() || isOtherLowercase()) {
return true
}
val index = binarySearchRange(casedStart, this)
return index >= 0 && this <= casedEnd[index]
}
@@ -101,9 +101,10 @@ external public actual fun Char.isISOControl(): Boolean
public actual fun Char.isWhitespace(): Boolean = isWhitespaceImpl()
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is upper case.
*
* A character is considered to be an upper case letter if its [category] is [CharCategory.UPPERCASE_LETTER].
* A character is considered to be an upper case character if its [category] is [CharCategory.UPPERCASE_LETTER],
* or it has contributory property Other_Uppercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isUpperCase
*/
@@ -118,9 +119,10 @@ public actual fun Char.isUpperCase(): Boolean {
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is lower case.
*
* A character is considered to be a lower case letter if its [category] is [CharCategory.LOWERCASE_LETTER].
* A character is considered to be a lower case character if its [category] is [CharCategory.LOWERCASE_LETTER],
* or it has contributory property Other_Lowercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isLowerCase
*/
@@ -51,17 +51,17 @@ internal fun Char.isLetterImpl(): Boolean {
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is a lower case letter, or it has contributory property Other_Lowercase.
*/
internal fun Char.isLowerCaseImpl(): Boolean {
return getLetterType() == 1
return getLetterType() == 1 || code.isOtherLowercase()
}
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is an upper case letter, or it has contributory property Other_Uppercase.
*/
internal fun Char.isUpperCaseImpl(): Boolean {
return getLetterType() == 2
return getLetterType() == 2 || code.isOtherUppercase()
}
/**
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
private object OtherLowercase {
internal val otherLowerStart = intArrayOf(
0x00aa, 0x00ba, 0x02b0, 0x02c0, 0x02e0, 0x0345, 0x037a, 0x1d2c, 0x1d78, 0x1d9b, 0x2071, 0x207f, 0x2090, 0x2170, 0x24d0, 0x2c7c, 0xa69c, 0xa770, 0xa7f8, 0xab5c,
)
internal val otherLowerLength = intArrayOf(
1, 1, 9, 2, 5, 1, 1, 63, 1, 37, 1, 1, 13, 16, 26, 2, 2, 1, 2, 4,
)
}
internal fun Int.isOtherLowercase(): Boolean {
val index = binarySearchRange(OtherLowercase.otherLowerStart, this)
return index >= 0 && this < OtherLowercase.otherLowerStart[index] + OtherLowercase.otherLowerLength[index]
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
internal fun Int.isOtherUppercase(): Boolean {
return this in 0x2160..0x216f
|| this in 0x24b6..0x24cf
}
@@ -51,17 +51,17 @@ internal fun Char.isLetterImpl(): Boolean {
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is a lower case letter, or it has contributory property Other_Lowercase.
*/
internal fun Char.isLowerCaseImpl(): Boolean {
return getLetterType() == 1
return getLetterType() == 1 || code.isOtherLowercase()
}
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is an upper case letter, or it has contributory property Other_Uppercase.
*/
internal fun Char.isUpperCaseImpl(): Boolean {
return getLetterType() == 2
return getLetterType() == 2 || code.isOtherUppercase()
}
/**
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
private object OtherLowercase {
internal val otherLowerStart = intArrayOf(
0x00aa, 0x00ba, 0x02b0, 0x02c0, 0x02e0, 0x0345, 0x037a, 0x1d2c, 0x1d78, 0x1d9b, 0x2071, 0x207f, 0x2090, 0x2170, 0x24d0, 0x2c7c, 0xa69c, 0xa770, 0xa7f8, 0xab5c,
)
internal val otherLowerLength = intArrayOf(
1, 1, 9, 2, 5, 1, 1, 63, 1, 37, 1, 1, 13, 16, 26, 2, 2, 1, 2, 4,
)
}
internal fun Int.isOtherLowercase(): Boolean {
val index = binarySearchRange(OtherLowercase.otherLowerStart, this)
return index >= 0 && this < OtherLowercase.otherLowerStart[index] + OtherLowercase.otherLowerLength[index]
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.text
//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
internal fun Int.isOtherUppercase(): Boolean {
return this in 0x2160..0x216f
|| this in 0x24b6..0x24cf
}
+6 -4
View File
@@ -181,9 +181,10 @@ public actual fun Char.isDigit(): Boolean {
}
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is upper case.
*
* A character is considered to be an upper case letter if its [category] is [CharCategory.UPPERCASE_LETTER].
* A character is considered to be an upper case character if its [category] is [CharCategory.UPPERCASE_LETTER],
* or it has contributory property Other_Uppercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isUpperCase
*/
@@ -199,9 +200,10 @@ public actual fun Char.isUpperCase(): Boolean {
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is lower case.
*
* A character is considered to be a lower case letter if its [category] is [CharCategory.LOWERCASE_LETTER].
* A character is considered to be a lower case character if its [category] is [CharCategory.LOWERCASE_LETTER],
* or it has contributory property Other_Lowercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isLowerCase
*/
@@ -100,7 +100,8 @@ public actual fun Char.isWhitespace(): Boolean = Character.isWhitespace(this) ||
/**
* Returns `true` if this character is upper case.
*
* @see Character.isUpperCase
* A character is considered to be an upper case character if its [category] is [CharCategory.UPPERCASE_LETTER],
* or it has contributory property Other_Uppercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isUpperCase
*/
@@ -110,7 +111,8 @@ public actual inline fun Char.isUpperCase(): Boolean = Character.isUpperCase(thi
/**
* Returns `true` if this character is lower case.
*
* @see Character.isLowerCase
* A character is considered to be a lower case character if its [category] is [CharCategory.LOWERCASE_LETTER],
* or it has contributory property Other_Lowercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isLowerCase
*/
+6 -4
View File
@@ -296,9 +296,10 @@ public expect fun Char.isLetterOrDigit(): Boolean
public expect fun Char.isDigit(): Boolean
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is upper case.
*
* A character is considered to be an upper case letter if its [category] is [CharCategory.UPPERCASE_LETTER].
* A character is considered to be an upper case character if its [category] is [CharCategory.UPPERCASE_LETTER],
* or it has contributory property Other_Uppercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isUpperCase
*/
@@ -306,9 +307,10 @@ public expect fun Char.isDigit(): Boolean
public expect fun Char.isUpperCase(): Boolean
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is lower case.
*
* A character is considered to be a lower case letter if its [category] is [CharCategory.LOWERCASE_LETTER].
* A character is considered to be a lower case character if its [category] is [CharCategory.LOWERCASE_LETTER],
* or it has contributory property Other_Lowercase as defined by the Unicode Standard.
*
* @sample samples.text.Chars.isLowerCase
*/
+31
View File
@@ -5,6 +5,7 @@
package test.text
import test.testOnNonJvm6And7
import kotlin.test.*
class CharTest {
@@ -521,4 +522,34 @@ class CharTest {
assertEquals("\uFFFF", '\uFFFF'.titlecase())
}
@Test
fun otherLowercaseProperty() {
testOnNonJvm6And7 {
val feminineOrdinalIndicator = '\u00AA'
assertTrue(feminineOrdinalIndicator.isLowerCase())
assertTrue(feminineOrdinalIndicator.isLetter())
assertFalse(feminineOrdinalIndicator.isUpperCase())
val circledLatinSmallLetterA = '\u24D0'
assertTrue(circledLatinSmallLetterA.isLowerCase())
assertFalse(circledLatinSmallLetterA.isLetter())
assertFalse(circledLatinSmallLetterA.isUpperCase())
}
}
@Test
fun otherUppercaseProperty() {
testOnNonJvm6And7 {
val romanNumberOne = '\u2160'
assertTrue(romanNumberOne.isUpperCase())
assertFalse(romanNumberOne.isLetter())
assertFalse(romanNumberOne.isLowerCase())
val circledLatinCapitalLetterZ = '\u24CF'
assertTrue(circledLatinCapitalLetterZ.isUpperCase())
assertFalse(circledLatinCapitalLetterZ.isLetter())
assertFalse(circledLatinCapitalLetterZ.isLowerCase())
}
}
}
@@ -19,8 +19,9 @@ internal fun FileWriter.writeHeader(file: File, pkg: String) {
appendLine(autoGeneratedWarning("GenerateUnicodeData.kt"))
}
internal fun FileWriter.writeIntArray(name: String, elements: List<Int>, strategy: RangesWritingStrategy) {
writeCollection(name, "intArrayOf", elements.map { it.toHexIntLiteral() }, strategy)
internal fun FileWriter.writeIntArray(name: String, elements: List<Int>, strategy: RangesWritingStrategy, useHex: Boolean = true) {
val toString = if (useHex) Int::toHexIntLiteral else Int::toString
writeCollection(name, "intArrayOf", elements.map(toString), strategy)
}
private fun FileWriter.writeCollection(
@@ -98,3 +99,16 @@ internal fun String.hexToInt(): Int {
internal fun List<String>.hexCharsToStringLiteral(): String {
return "\"${joinToString(separator = "") { "\\u$it" }}\""
}
internal fun IntRange.rangeCheck(ch: String, indent: String): String {
val firstHex = first.toHexIntLiteral()
val lastHex = last.toHexIntLiteral()
return when (first) {
last ->
"$ch == $firstHex"
last - 1 ->
"$ch == $firstHex\n$indent|| $ch == $lastHex"
else ->
"$ch in $firstHex..$lastHex"
}
}
@@ -12,6 +12,8 @@ import generators.unicode.ranges.RangesGenerator
import generators.unicode.mappings.string.StringCasingTestGenerator
import generators.unicode.mappings.string.StringLowercaseGenerator
import generators.unicode.mappings.string.StringUppercaseGenerator
import generators.unicode.ranges.OtherLowercaseRangesGenerator
import generators.unicode.ranges.OtherUppercaseRangesGenerator
import templates.COPYRIGHT_NOTICE
import templates.KotlinTarget
import templates.readCopyrightNoticeFromProfile
@@ -61,6 +63,8 @@ fun main(args: Array<String>) {
}
val categoryRangesGenerators = mutableListOf<RangesGenerator>()
val otherLowercaseGenerators = mutableListOf<OtherLowercaseRangesGenerator>()
val otherUppercaseGenerators = mutableListOf<OtherUppercaseRangesGenerator>()
fun addRangesGenerators(generatedDir: File, target: KotlinTarget) {
val category = RangesGenerator.forCharCategory(generatedDir.resolve("_CharCategories.kt"), target)
@@ -71,6 +75,9 @@ fun main(args: Array<String>) {
categoryRangesGenerators.add(digit)
categoryRangesGenerators.add(letter)
categoryRangesGenerators.add(whitespace)
otherLowercaseGenerators.add(OtherLowercaseRangesGenerator(generatedDir.resolve("_OtherLowercaseChars.kt"), target))
otherUppercaseGenerators.add(OtherUppercaseRangesGenerator(generatedDir.resolve("_OtherUppercaseChars.kt"), target))
}
val oneToOneMappingsGenerators = mutableListOf<MappingsGenerator>()
@@ -154,9 +161,18 @@ fun main(args: Array<String>) {
bmpUnicodeDataLines.forEach { line -> it.appendLine(line) }
it.generate()
}
otherLowercaseGenerators.forEach {
propListLines.forEach { line -> it.appendLine(line) }
it.generate()
}
otherUppercaseGenerators.forEach {
propListLines.forEach { line -> it.appendLine(line) }
it.generate()
}
categoryTestGenerator.let {
bmpUnicodeDataLines.forEach { line -> it.appendLine(line) }
propListLines.forEach { line -> it.appendPropertyLine(line) }
it.generate()
}
@@ -176,7 +192,6 @@ fun main(args: Array<String>) {
}
stringLowercaseGenerator.let {
specialCasingLines.forEach { line -> it.appendSpecialCasingLine(line) }
propListLines.forEach { line -> it.appendPropListLine(line) }
wordBreakPropertyLines.forEach { line -> it.appendWordBreakPropertyLine(line) }
it.generate()
}
@@ -14,6 +14,14 @@ internal class PropertyLine(properties: List<String>) {
val rangeEnd: String = properties[0].split("..").last()
val property: String = properties[1].takeWhile { it != ' ' }
fun intRange(): IntRange {
return rangeStart.hexToInt()..rangeEnd.hexToInt()
}
fun hexIntRangeLiteral(): String {
return "${rangeStart.hexToInt().toHexIntLiteral()}..${rangeEnd.hexToInt().toHexIntLiteral()}"
}
override fun toString(): String {
return "PropertyLine{rangeStart=$rangeStart" +
", rangeEnd=$rangeEnd" +
@@ -57,9 +57,7 @@ internal class StringCasingTestGenerator(private val outputDir: File) {
writer.appendLine("@SharedImmutable")
writer.appendLine("private val $rangesArrayName = arrayOf<IntRange>(")
ranges.forEach {
val start = it.rangeStart.hexToInt().toHexIntLiteral()
val end = it.rangeEnd.hexToInt().toHexIntLiteral()
writer.appendLine(" $start..$end,")
writer.appendLine(" ${it.hexIntRangeLiteral()},")
}
writer.appendLine(")")
writer.appendLine()
@@ -44,18 +44,11 @@ internal class StringLowercaseGenerator(
override fun UnicodeDataLine.mapping(): String = lowercaseMapping
fun appendPropListLine(line: PropertyLine) {
when (line.property) {
"Other_Lowercase",
"Other_Uppercase" -> casedRanges.add(line.rangeStart.hexToInt()..line.rangeEnd.hexToInt())
}
}
fun appendWordBreakPropertyLine(line: PropertyLine) {
when (line.property) {
"MidLetter",
"MidNumLet",
"Single_Quote" -> caseIgnorableRanges.add(line.rangeStart.hexToInt()..line.rangeEnd.hexToInt())
"Single_Quote" -> caseIgnorableRanges.add(line.intRange())
}
}
@@ -101,6 +94,9 @@ internal class StringLowercaseGenerator(
CharCategory.TITLECASE_LETTER.value -> return true
}
}
if (isOtherUppercase() || isOtherLowercase()) {
return true
}
val index = binarySearchRange(casedStart, this)
return index >= 0 && this <= casedEnd[index]
}
@@ -5,6 +5,7 @@
package generators.unicode.ranges
import generators.unicode.PropertyLine
import generators.unicode.UnicodeDataLine
import generators.unicode.writeHeader
import java.io.File
@@ -38,6 +39,16 @@ internal class CharCategoryTestGenerator(private val outputFile: File) {
}
}
private val otherLowercaseRanges = mutableListOf<PropertyLine>()
private val otherUppercaseRanges = mutableListOf<PropertyLine>()
fun appendPropertyLine(line: PropertyLine) {
when (line.property) {
"Other_Lowercase" -> otherLowercaseRanges.add(line)
"Other_Uppercase" -> otherUppercaseRanges.add(line)
}
}
fun generate() {
writer?.appendLine(")")
writer?.close()
@@ -111,10 +122,10 @@ class CharCategoryTest {
val expectedIsLetterOrDigit = expectedIsLetter || expectedIsDigit
test(expectedIsLetterOrDigit, char.isLetterOrDigit(), "isLetterOrDigit()")
val expectedIsLowerCase = isLowerCase(expectedCategoryCode)
val expectedIsLowerCase = isLowerCase(char, expectedCategoryCode)
test(expectedIsLowerCase, char.isLowerCase(), "isLowerCase()")
val expectedIsUpperCase = isUpperCase(expectedCategoryCode)
val expectedIsUpperCase = isUpperCase(char, expectedCategoryCode)
test(expectedIsUpperCase, char.isUpperCase(), "isUpperCase()")
val expectedIsWhitespace = isWhitespace(char, expectedCategoryCode)
@@ -136,12 +147,20 @@ class CharCategoryTest {
).map { it.code }
}
private fun isLowerCase(categoryCode: String): Boolean {
return categoryCode == CharCategory.LOWERCASE_LETTER.code
private val otherLowerChars = listOf<IntRange>(
${otherLowercaseRanges.joinToString { it.hexIntRangeLiteral() }}
).flatten().toHashSet()
private fun isLowerCase(char: Char, categoryCode: String): Boolean {
return categoryCode == CharCategory.LOWERCASE_LETTER.code || otherLowerChars.contains(char.code)
}
private fun isUpperCase(categoryCode: String): Boolean {
return categoryCode == CharCategory.UPPERCASE_LETTER.code
private val otherUpperChars = listOf<IntRange>(
${otherUppercaseRanges.joinToString { it.hexIntRangeLiteral() }}
).flatten().toHashSet()
private fun isUpperCase(char: Char, categoryCode: String): Boolean {
return categoryCode == CharCategory.UPPERCASE_LETTER.code || otherUpperChars.contains(char.code)
}
private fun isWhitespace(char: Char, categoryCode: String): Boolean {
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package generators.unicode.ranges
import generators.unicode.PropertyLine
import generators.unicode.hexToInt
import generators.unicode.writeHeader
import generators.unicode.writeIntArray
import templates.KotlinTarget
import java.io.File
import java.io.FileWriter
internal class OtherLowercaseRangesGenerator(
private val outputFile: File,
private val target: KotlinTarget
) {
private val otherLowerRanges = mutableListOf<IntRange>()
fun appendLine(line: PropertyLine) {
// In Native the Other_Lowercase code points are also used to perform String.lowercase()
if (target != KotlinTarget.Native && line.rangeStart.hexToInt() > 0xFFFF) return
if (line.property == "Other_Lowercase") {
otherLowerRanges.add(line.intRange())
}
}
fun generate() {
val strategy = RangesWritingStrategy.of(target, "OtherLowercase")
FileWriter(outputFile).use { writer ->
writer.writeHeader(outputFile, "kotlin.text")
writer.appendLine()
strategy.beforeWritingRanges(writer)
writer.writeIntArray("otherLowerStart", otherLowerRanges.map { it.first }, strategy)
writer.writeIntArray("otherLowerLength", otherLowerRanges.map { it.last - it.first + 1 }, strategy, useHex = false)
strategy.afterWritingRanges(writer)
writer.appendLine()
writer.appendLine(isOtherLowercaseImpl(strategy))
}
}
fun isOtherLowercaseImpl(strategy: RangesWritingStrategy) = """
internal fun Int.isOtherLowercase(): Boolean {
val index = binarySearchRange(${strategy.rangeRef("otherLowerStart")}, this)
return index >= 0 && this < ${strategy.rangeRef("otherLowerStart")}[index] + ${strategy.rangeRef("otherLowerLength")}[index]
}
""".trimIndent()
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package generators.unicode.ranges
import generators.unicode.PropertyLine
import generators.unicode.hexToInt
import generators.unicode.rangeCheck
import generators.unicode.writeHeader
import templates.KotlinTarget
import java.io.File
import java.io.FileWriter
internal class OtherUppercaseRangesGenerator(
private val outputFile: File,
private val target: KotlinTarget
) {
private val otherUpperRanges = mutableListOf<IntRange>()
fun appendLine(line: PropertyLine) {
// In Native the Other_Uppercase code points are also used to perform String.lowercase()
if (target != KotlinTarget.Native && line.rangeStart.hexToInt() > 0xFFFF) return
if (line.property == "Other_Uppercase") {
otherUpperRanges.add(line.intRange())
}
}
fun generate() {
FileWriter(outputFile).use { writer ->
writer.writeHeader(outputFile, "kotlin.text")
writer.appendLine()
writer.appendLine(isOtherUppercaseImpl())
}
}
fun isOtherUppercaseImpl(): String {
val indent = " ".repeat(5)
val builder = StringBuilder()
for (i in otherUpperRanges.indices) {
if (i != 0) {
builder.appendLine().append(indent).append("|| ")
}
builder.append(otherUpperRanges[i].rangeCheck("this", indent))
}
return """
internal fun Int.isOtherUppercase(): Boolean {
return $builder
}
""".trimIndent()
}
}
@@ -60,17 +60,17 @@ internal open class LetterRangesWriter(protected val strategy: RangesWritingStra
}
/**
* Returns `true` if this character is a lower case letter.
* Returns `true` if this character is a lower case letter, or it has contributory property Other_Lowercase.
*/
internal fun Char.isLowerCaseImpl(): Boolean {
return getLetterType() == 1
return getLetterType() == 1 || code.isOtherLowercase()
}
/**
* Returns `true` if this character is an upper case letter.
* Returns `true` if this character is an upper case letter, or it has contributory property Other_Uppercase.
*/
internal fun Char.isUpperCaseImpl(): Boolean {
return getLetterType() == 2
return getLetterType() == 2 || code.isOtherUppercase()
}
/**
@@ -5,6 +5,7 @@
package generators.unicode.ranges.writers
import generators.unicode.rangeCheck
import generators.unicode.toHexIntLiteral
import java.io.FileWriter
@@ -38,24 +39,12 @@ internal class WhitespaceRangesWriter : RangesWriter {
val start = rangeStart[i]
val end = rangeEnd[i]
when (start) {
end -> {
if (start > 0x1000 && tabCount == 5) {
builder.appendLine("$ch > 0x1000 && (")
tabCount = 6
builder.append(tab.repeat(tabCount))
}
builder.appendLine("$ch == ${start.toHexIntLiteral()}")
}
end - 1 -> {
builder.appendLine("$ch == ${start.toHexIntLiteral()}")
builder.append(tab.repeat(tabCount)).append("|| ")
builder.appendLine("$ch == ${end.toHexIntLiteral()}")
}
else -> {
builder.appendLine("$ch in ${start.toHexIntLiteral()}..${end.toHexIntLiteral()}")
}
if (start > 0x1000 && tabCount == 5) {
builder.appendLine("$ch > 0x1000 && (")
tabCount = 6
builder.append(tab.repeat(tabCount))
}
builder.appendLine((start..end).rangeCheck(ch, tab.repeat(tabCount)))
}
return builder.append(tab.repeat(5)).append(")").toString()